Merge pull request #5552 from gytis/spring-boot-starter-jta-narayana
* spring-boot-starter-jta-narayana: Polish contribution Add Narayana JTA support
This commit is contained in:
		
						commit
						69a80596ba
					
				| 
						 | 
				
			
			@ -577,6 +577,16 @@
 | 
			
		|||
			<artifactId>jooq</artifactId>
 | 
			
		||||
			<optional>true</optional>
 | 
			
		||||
		</dependency>
 | 
			
		||||
		<dependency>
 | 
			
		||||
			<groupId>org.jboss.narayana.jta</groupId>
 | 
			
		||||
			<artifactId>jta</artifactId>
 | 
			
		||||
			<optional>true</optional>
 | 
			
		||||
		</dependency>
 | 
			
		||||
		<dependency>
 | 
			
		||||
			<groupId>org.jboss.narayana.jts</groupId>
 | 
			
		||||
			<artifactId>narayana-jts-integration</artifactId>
 | 
			
		||||
			<optional>true</optional>
 | 
			
		||||
		</dependency>
 | 
			
		||||
		<!-- Annotation processing -->
 | 
			
		||||
		<dependency>
 | 
			
		||||
			<groupId>org.springframework.boot</groupId>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -40,7 +40,7 @@ import org.springframework.context.annotation.Import;
 | 
			
		|||
		ActiveMQAutoConfiguration.class, HornetQAutoConfiguration.class,
 | 
			
		||||
		HibernateJpaAutoConfiguration.class })
 | 
			
		||||
@Import({ JndiJtaConfiguration.class, BitronixJtaConfiguration.class,
 | 
			
		||||
		AtomikosJtaConfiguration.class })
 | 
			
		||||
		AtomikosJtaConfiguration.class, NarayanaJtaConfiguration.class })
 | 
			
		||||
@EnableConfigurationProperties(JtaProperties.class)
 | 
			
		||||
public class JtaAutoConfiguration {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,142 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2016 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.springframework.boot.autoconfigure.transaction.jta;
 | 
			
		||||
 | 
			
		||||
import javax.jms.Message;
 | 
			
		||||
import javax.transaction.TransactionManager;
 | 
			
		||||
import javax.transaction.UserTransaction;
 | 
			
		||||
 | 
			
		||||
import com.arjuna.ats.jbossatx.jta.RecoveryManagerService;
 | 
			
		||||
 | 
			
		||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
 | 
			
		||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 | 
			
		||||
import org.springframework.boot.jta.XAConnectionFactoryWrapper;
 | 
			
		||||
import org.springframework.boot.jta.XADataSourceWrapper;
 | 
			
		||||
import org.springframework.boot.jta.narayana.NarayanaBeanFactoryPostProcessor;
 | 
			
		||||
import org.springframework.boot.jta.narayana.NarayanaConfigurationBean;
 | 
			
		||||
import org.springframework.boot.jta.narayana.NarayanaProperties;
 | 
			
		||||
import org.springframework.boot.jta.narayana.NarayanaRecoveryManagerBean;
 | 
			
		||||
import org.springframework.boot.jta.narayana.NarayanaXAConnectionFactoryWrapper;
 | 
			
		||||
import org.springframework.boot.jta.narayana.NarayanaXADataSourceWrapper;
 | 
			
		||||
import org.springframework.context.annotation.Bean;
 | 
			
		||||
import org.springframework.context.annotation.Configuration;
 | 
			
		||||
import org.springframework.context.annotation.DependsOn;
 | 
			
		||||
import org.springframework.transaction.PlatformTransactionManager;
 | 
			
		||||
import org.springframework.transaction.jta.JtaTransactionManager;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * JTA Configuration for <a href="http://narayana.io/">Narayana</a>.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Gytis Trikleris
 | 
			
		||||
 * @since 1.4.0
 | 
			
		||||
 */
 | 
			
		||||
@Configuration
 | 
			
		||||
@ConditionalOnClass({ JtaTransactionManager.class,
 | 
			
		||||
		com.arjuna.ats.jta.UserTransaction.class })
 | 
			
		||||
@ConditionalOnMissingBean(PlatformTransactionManager.class)
 | 
			
		||||
public class NarayanaJtaConfiguration {
 | 
			
		||||
 | 
			
		||||
	private final JtaProperties jtaProperties;
 | 
			
		||||
 | 
			
		||||
	public NarayanaJtaConfiguration(JtaProperties jtaProperties) {
 | 
			
		||||
		this.jtaProperties = jtaProperties;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Bean
 | 
			
		||||
	@ConditionalOnMissingBean
 | 
			
		||||
	public NarayanaProperties narayanaProperties() {
 | 
			
		||||
		return new NarayanaProperties();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Bean
 | 
			
		||||
	@ConditionalOnMissingBean
 | 
			
		||||
	public NarayanaConfigurationBean narayanaConfiguration(
 | 
			
		||||
			NarayanaProperties properties) {
 | 
			
		||||
		if (this.jtaProperties.getLogDir() != null) {
 | 
			
		||||
			properties.setLogDir(this.jtaProperties.getLogDir());
 | 
			
		||||
		}
 | 
			
		||||
		if (this.jtaProperties.getTransactionManagerId() != null) {
 | 
			
		||||
			properties.setTransactionManagerId(
 | 
			
		||||
					this.jtaProperties.getTransactionManagerId());
 | 
			
		||||
		}
 | 
			
		||||
		return new NarayanaConfigurationBean(properties);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Bean
 | 
			
		||||
	@DependsOn("narayanaConfiguration")
 | 
			
		||||
	@ConditionalOnMissingBean
 | 
			
		||||
	public UserTransaction narayanaUserTransaction() {
 | 
			
		||||
		return com.arjuna.ats.jta.UserTransaction.userTransaction();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Bean
 | 
			
		||||
	@DependsOn("narayanaConfiguration")
 | 
			
		||||
	@ConditionalOnMissingBean
 | 
			
		||||
	public TransactionManager narayanaTransactionManager() {
 | 
			
		||||
		return com.arjuna.ats.jta.TransactionManager.transactionManager();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Bean
 | 
			
		||||
	@DependsOn("narayanaConfiguration")
 | 
			
		||||
	public RecoveryManagerService narayanaRecoveryManagerService() {
 | 
			
		||||
		return new RecoveryManagerService();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Bean
 | 
			
		||||
	public NarayanaRecoveryManagerBean narayanaRecoveryManager(
 | 
			
		||||
			RecoveryManagerService recoveryManagerService) {
 | 
			
		||||
		return new NarayanaRecoveryManagerBean(recoveryManagerService);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Bean
 | 
			
		||||
	public JtaTransactionManager transactionManager(UserTransaction userTransaction,
 | 
			
		||||
			TransactionManager transactionManager) {
 | 
			
		||||
		return new JtaTransactionManager(userTransaction, transactionManager);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Bean
 | 
			
		||||
	@ConditionalOnMissingBean(XADataSourceWrapper.class)
 | 
			
		||||
	public XADataSourceWrapper xaDataSourceWrapper(
 | 
			
		||||
			NarayanaRecoveryManagerBean narayanaRecoveryManagerBean,
 | 
			
		||||
			NarayanaProperties narayanaProperties) {
 | 
			
		||||
		return new NarayanaXADataSourceWrapper(narayanaRecoveryManagerBean,
 | 
			
		||||
				narayanaProperties);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Bean
 | 
			
		||||
	@ConditionalOnMissingBean
 | 
			
		||||
	public static NarayanaBeanFactoryPostProcessor narayanaBeanFactoryPostProcessor() {
 | 
			
		||||
		return new NarayanaBeanFactoryPostProcessor();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Configuration
 | 
			
		||||
	@ConditionalOnClass(Message.class)
 | 
			
		||||
	static class NarayanaJtaJmsConfiguration {
 | 
			
		||||
 | 
			
		||||
		@Bean
 | 
			
		||||
		@ConditionalOnMissingBean(XAConnectionFactoryWrapper.class)
 | 
			
		||||
		public NarayanaXAConnectionFactoryWrapper xaConnectionFactoryWrapper(
 | 
			
		||||
				TransactionManager transactionManager,
 | 
			
		||||
				NarayanaRecoveryManagerBean narayanaRecoveryManagerBean,
 | 
			
		||||
				NarayanaProperties narayanaProperties) {
 | 
			
		||||
			return new NarayanaXAConnectionFactoryWrapper(transactionManager,
 | 
			
		||||
					narayanaRecoveryManagerBean, narayanaProperties);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -100,6 +100,7 @@
 | 
			
		|||
		<jaxen.version>1.1.6</jaxen.version>
 | 
			
		||||
		<jaybird.version>2.2.10</jaybird.version>
 | 
			
		||||
		<jboss-logging.version>3.3.0.Final</jboss-logging.version>
 | 
			
		||||
		<jboss-transaction-spi.version>7.3.0.Final</jboss-transaction-spi.version>
 | 
			
		||||
		<jdom2.version>2.0.6</jdom2.version>
 | 
			
		||||
		<jedis.version>2.8.1</jedis.version>
 | 
			
		||||
		<jersey.version>2.22.2</jersey.version>
 | 
			
		||||
| 
						 | 
				
			
			@ -124,6 +125,7 @@
 | 
			
		|||
		<mockito.version>1.10.19</mockito.version>
 | 
			
		||||
		<mongodb.version>2.14.2</mongodb.version>
 | 
			
		||||
		<mysql.version>5.1.38</mysql.version>
 | 
			
		||||
		<narayana.version>5.3.2.Final</narayana.version>
 | 
			
		||||
		<nekohtml.version>1.9.22</nekohtml.version>
 | 
			
		||||
		<neo4j-ogm.version>2.0.0</neo4j-ogm.version>
 | 
			
		||||
		<postgresql.version>9.4.1208.jre7</postgresql.version>
 | 
			
		||||
| 
						 | 
				
			
			@ -429,6 +431,11 @@
 | 
			
		|||
				<artifactId>spring-boot-starter-mustache</artifactId>
 | 
			
		||||
				<version>1.4.0.BUILD-SNAPSHOT</version>
 | 
			
		||||
			</dependency>
 | 
			
		||||
			<dependency>
 | 
			
		||||
				<groupId>org.springframework.boot</groupId>
 | 
			
		||||
				<artifactId>spring-boot-starter-jta-narayana</artifactId>
 | 
			
		||||
				<version>1.4.0.BUILD-SNAPSHOT</version>
 | 
			
		||||
			</dependency>
 | 
			
		||||
			<dependency>
 | 
			
		||||
				<groupId>org.springframework.boot</groupId>
 | 
			
		||||
				<artifactId>spring-boot-starter-remote-shell</artifactId>
 | 
			
		||||
| 
						 | 
				
			
			@ -1733,11 +1740,36 @@
 | 
			
		|||
				<artifactId>javassist</artifactId>
 | 
			
		||||
				<version>${javassist.version}</version>
 | 
			
		||||
			</dependency>
 | 
			
		||||
			<dependency>
 | 
			
		||||
				<groupId>org.jboss</groupId>
 | 
			
		||||
				<artifactId>jboss-transaction-spi</artifactId>
 | 
			
		||||
				<version>${jboss-transaction-spi.version}</version>
 | 
			
		||||
			</dependency>
 | 
			
		||||
			<dependency>
 | 
			
		||||
				<groupId>org.jboss.logging</groupId>
 | 
			
		||||
				<artifactId>jboss-logging</artifactId>
 | 
			
		||||
				<version>${jboss-logging.version}</version>
 | 
			
		||||
			</dependency>
 | 
			
		||||
			<dependency>
 | 
			
		||||
				<groupId>org.jboss.narayana.jta</groupId>
 | 
			
		||||
				<artifactId>jdbc</artifactId>
 | 
			
		||||
				<version>${narayana.version}</version>
 | 
			
		||||
			</dependency>
 | 
			
		||||
			<dependency>
 | 
			
		||||
				<groupId>org.jboss.narayana.jta</groupId>
 | 
			
		||||
				<artifactId>jms</artifactId>
 | 
			
		||||
				<version>${narayana.version}</version>
 | 
			
		||||
			</dependency>
 | 
			
		||||
			<dependency>
 | 
			
		||||
				<groupId>org.jboss.narayana.jta</groupId>
 | 
			
		||||
				<artifactId>jta</artifactId>
 | 
			
		||||
				<version>${narayana.version}</version>
 | 
			
		||||
			</dependency>
 | 
			
		||||
			<dependency>
 | 
			
		||||
				<groupId>org.jboss.narayana.jts</groupId>
 | 
			
		||||
				<artifactId>narayana-jts-integration</artifactId>
 | 
			
		||||
				<version>${narayana.version}</version>
 | 
			
		||||
			</dependency>
 | 
			
		||||
			<dependency>
 | 
			
		||||
				<groupId>org.jdom</groupId>
 | 
			
		||||
				<artifactId>jdom2</artifactId>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -715,6 +715,19 @@ content into your application; rather pick only the properties that you need.
 | 
			
		|||
    spring.jta.bitronix.properties.skip-corrupted-logs=false # Skip corrupted transactions log entries.
 | 
			
		||||
    spring.jta.bitronix.properties.warn-about-zero-resource-transaction=true # Log a warning for transactions executed without a single enlisted resource.
 | 
			
		||||
 | 
			
		||||
    # NARAYANA
 | 
			
		||||
    spring.jta.narayana.default-timeout=60 # Set default transaction timeout in seconds
 | 
			
		||||
    spring.jta.narayana.expiry-scanners=com.arjuna.ats.internal.arjuna.recovery.ExpiredTransactionStatusManagerScanner # List of ExpiryScanner implementations
 | 
			
		||||
    spring.jta.narayana.one-phase-commit=true # Enable or disable one phase commit optimisation
 | 
			
		||||
    spring.jta.narayana.periodic-recovery-period=120 # Set interval in which periodic recovery scans are performed in seconds
 | 
			
		||||
    spring.jta.narayana.recovery-backoff-period=10 # Set back off period between first and second phases of the recovery scan in seconds
 | 
			
		||||
    spring.jta.narayana.recovery-db-user= # Database username to be used by recovery manager
 | 
			
		||||
    spring.jta.narayana.recovery-db-pass= # Database password to be used by recovery manager
 | 
			
		||||
    spring.jta.narayana.recovery-jms-user= # JMS username to be used by recovery manager
 | 
			
		||||
    spring.jta.narayana.recovery-jms-pass= # JMS password to be used by recovery manager
 | 
			
		||||
    spring.jta.narayana.recovery-modules=com.arjuna.ats.internal.arjuna.recovery.AtomicActionRecoveryModule,com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule # List of RecoveryModule implementations
 | 
			
		||||
    spring.jta.narayana.xa-resource-orphan-filters=com.arjuna.ats.internal.jta.recovery.arjunacore.JTATransactionLogXAResourceOrphanFilter,com.arjuna.ats.internal.jta.recovery.arjunacore.JTANodeNameXAResourceOrphanFilter # List of XAResourceOrphanFilter implementations
 | 
			
		||||
 | 
			
		||||
	# EMBEDDED MONGODB ({sc-spring-boot-autoconfigure}/mongo/embedded/EmbeddedMongoProperties.{sc-ext}[EmbeddedMongoProperties])
 | 
			
		||||
	spring.mongodb.embedded.features=SYNC_DELAY # Comma-separated list of features to enable.
 | 
			
		||||
	spring.mongodb.embedded.version=2.6.10 # Version of Mongo to use.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -63,6 +63,7 @@
 | 
			
		|||
		<module>spring-boot-sample-jpa</module>
 | 
			
		||||
		<module>spring-boot-sample-jta-atomikos</module>
 | 
			
		||||
		<module>spring-boot-sample-jta-bitronix</module>
 | 
			
		||||
		<module>spring-boot-sample-jta-narayana</module>
 | 
			
		||||
		<module>spring-boot-sample-jta-jndi</module>
 | 
			
		||||
		<module>spring-boot-sample-liquibase</module>
 | 
			
		||||
		<module>spring-boot-sample-logback</module>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,56 @@
 | 
			
		|||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 | 
			
		||||
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 | 
			
		||||
	<modelVersion>4.0.0</modelVersion>
 | 
			
		||||
	<parent>
 | 
			
		||||
		<artifactId>spring-boot-samples</artifactId>
 | 
			
		||||
		<groupId>org.springframework.boot</groupId>
 | 
			
		||||
		<version>1.4.0.BUILD-SNAPSHOT</version>
 | 
			
		||||
	</parent>
 | 
			
		||||
	<artifactId>spring-boot-sample-jta-narayana</artifactId>
 | 
			
		||||
	<name>Spring Boot Narayana JTA Sample</name>
 | 
			
		||||
	<description>Spring Boot Narayana JTA Sample</description>
 | 
			
		||||
	<url>http://projects.spring.io/spring-boot/</url>
 | 
			
		||||
	<properties>
 | 
			
		||||
		<main.basedir>${basedir}/../..</main.basedir>
 | 
			
		||||
	</properties>
 | 
			
		||||
	<dependencies>
 | 
			
		||||
		<dependency>
 | 
			
		||||
			<groupId>org.springframework</groupId>
 | 
			
		||||
			<artifactId>spring-jms</artifactId>
 | 
			
		||||
		</dependency>
 | 
			
		||||
		<dependency>
 | 
			
		||||
			<groupId>org.springframework.boot</groupId>
 | 
			
		||||
			<artifactId>spring-boot-starter-data-jpa</artifactId>
 | 
			
		||||
		</dependency>
 | 
			
		||||
		<dependency>
 | 
			
		||||
			<groupId>org.springframework.boot</groupId>
 | 
			
		||||
			<artifactId>spring-boot-starter-jta-narayana</artifactId>
 | 
			
		||||
		</dependency>
 | 
			
		||||
		<dependency>
 | 
			
		||||
			<groupId>org.springframework.boot</groupId>
 | 
			
		||||
			<artifactId>spring-boot-starter-hornetq</artifactId>
 | 
			
		||||
		</dependency>
 | 
			
		||||
		<dependency>
 | 
			
		||||
			<groupId>org.hornetq</groupId>
 | 
			
		||||
			<artifactId>hornetq-jms-server</artifactId>
 | 
			
		||||
		</dependency>
 | 
			
		||||
		<dependency>
 | 
			
		||||
			<groupId>com.h2database</groupId>
 | 
			
		||||
			<artifactId>h2</artifactId>
 | 
			
		||||
		</dependency>
 | 
			
		||||
		<dependency>
 | 
			
		||||
			<groupId>org.springframework.boot</groupId>
 | 
			
		||||
			<artifactId>spring-boot-starter-test</artifactId>
 | 
			
		||||
			<scope>test</scope>
 | 
			
		||||
		</dependency>
 | 
			
		||||
	</dependencies>
 | 
			
		||||
	<build>
 | 
			
		||||
		<plugins>
 | 
			
		||||
			<plugin>
 | 
			
		||||
				<groupId>org.springframework.boot</groupId>
 | 
			
		||||
				<artifactId>spring-boot-maven-plugin</artifactId>
 | 
			
		||||
			</plugin>
 | 
			
		||||
		</plugins>
 | 
			
		||||
	</build>
 | 
			
		||||
</project>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,43 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2016 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package sample.narayana;
 | 
			
		||||
 | 
			
		||||
import javax.persistence.Entity;
 | 
			
		||||
import javax.persistence.GeneratedValue;
 | 
			
		||||
import javax.persistence.Id;
 | 
			
		||||
 | 
			
		||||
@Entity
 | 
			
		||||
public class Account {
 | 
			
		||||
 | 
			
		||||
	@Id
 | 
			
		||||
	@GeneratedValue
 | 
			
		||||
	private Long id;
 | 
			
		||||
 | 
			
		||||
	private String username;
 | 
			
		||||
 | 
			
		||||
	Account() {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public Account(String username) {
 | 
			
		||||
		this.username = username;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public String getUsername() {
 | 
			
		||||
		return this.username;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,23 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2016 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package sample.narayana;
 | 
			
		||||
 | 
			
		||||
import org.springframework.data.jpa.repository.JpaRepository;
 | 
			
		||||
 | 
			
		||||
public interface AccountRepository extends JpaRepository<Account, Long> {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,47 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2016 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package sample.narayana;
 | 
			
		||||
 | 
			
		||||
import javax.transaction.Transactional;
 | 
			
		||||
 | 
			
		||||
import org.springframework.beans.factory.annotation.Autowired;
 | 
			
		||||
import org.springframework.jms.core.JmsTemplate;
 | 
			
		||||
import org.springframework.stereotype.Service;
 | 
			
		||||
 | 
			
		||||
@Service
 | 
			
		||||
@Transactional
 | 
			
		||||
public class AccountService {
 | 
			
		||||
 | 
			
		||||
	private final JmsTemplate jmsTemplate;
 | 
			
		||||
 | 
			
		||||
	private final AccountRepository accountRepository;
 | 
			
		||||
 | 
			
		||||
	@Autowired
 | 
			
		||||
	public AccountService(JmsTemplate jmsTemplate, AccountRepository accountRepository) {
 | 
			
		||||
		this.jmsTemplate = jmsTemplate;
 | 
			
		||||
		this.accountRepository = accountRepository;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void createAccountAndNotify(String username) {
 | 
			
		||||
		this.jmsTemplate.convertAndSend("accounts", username);
 | 
			
		||||
		this.accountRepository.save(new Account(username));
 | 
			
		||||
		if ("error".equals(username)) {
 | 
			
		||||
			throw new SampleRuntimeException("Simulated error");
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,30 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2016 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package sample.narayana;
 | 
			
		||||
 | 
			
		||||
import org.springframework.jms.annotation.JmsListener;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
 | 
			
		||||
@Component
 | 
			
		||||
public class Messages {
 | 
			
		||||
 | 
			
		||||
	@JmsListener(destination = "accounts")
 | 
			
		||||
	public void onMessage(String content) {
 | 
			
		||||
		System.out.println("----> " + content);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,47 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2016 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package sample.narayana;
 | 
			
		||||
 | 
			
		||||
import java.io.Closeable;
 | 
			
		||||
 | 
			
		||||
import org.springframework.boot.SpringApplication;
 | 
			
		||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
 | 
			
		||||
import org.springframework.context.ApplicationContext;
 | 
			
		||||
 | 
			
		||||
@SpringBootApplication
 | 
			
		||||
public class SampleNarayanaApplication {
 | 
			
		||||
 | 
			
		||||
	public static void main(String[] args) throws Exception {
 | 
			
		||||
		ApplicationContext context = SpringApplication
 | 
			
		||||
				.run(SampleNarayanaApplication.class, args);
 | 
			
		||||
		AccountService service = context.getBean(AccountService.class);
 | 
			
		||||
		AccountRepository repository = context.getBean(AccountRepository.class);
 | 
			
		||||
		service.createAccountAndNotify("josh");
 | 
			
		||||
		System.out.println("Count is " + repository.count());
 | 
			
		||||
		try {
 | 
			
		||||
			// Using username "error" will cause service to throw SampleRuntimeException
 | 
			
		||||
			service.createAccountAndNotify("error");
 | 
			
		||||
		}
 | 
			
		||||
		catch (SampleRuntimeException ex) {
 | 
			
		||||
			// Log message to let test case know that exception was thrown
 | 
			
		||||
			System.out.println(ex.getMessage());
 | 
			
		||||
		}
 | 
			
		||||
		System.out.println("Count is " + repository.count());
 | 
			
		||||
		((Closeable) context).close();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,25 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2016 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package sample.narayana;
 | 
			
		||||
 | 
			
		||||
public class SampleRuntimeException extends RuntimeException {
 | 
			
		||||
 | 
			
		||||
	public SampleRuntimeException(String message) {
 | 
			
		||||
		super(message);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
spring.hornetq.mode=embedded
 | 
			
		||||
spring.hornetq.embedded.enabled=true
 | 
			
		||||
spring.hornetq.embedded.queues=accounts
 | 
			
		||||
 | 
			
		||||
logging.level.com.arjuna=INFO
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,65 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2016 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package sample.narayana;
 | 
			
		||||
 | 
			
		||||
import org.assertj.core.api.Condition;
 | 
			
		||||
import org.junit.Rule;
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
 | 
			
		||||
import org.springframework.boot.test.rule.OutputCapture;
 | 
			
		||||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Basic integration tests for demo application.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Gytis Trikleris
 | 
			
		||||
 */
 | 
			
		||||
public class SampleNarayanaApplicationTests {
 | 
			
		||||
 | 
			
		||||
	@Rule
 | 
			
		||||
	public OutputCapture outputCapture = new OutputCapture();
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void testTransactionRollback() throws Exception {
 | 
			
		||||
		SampleNarayanaApplication.main(new String[] {});
 | 
			
		||||
		String output = this.outputCapture.toString();
 | 
			
		||||
		assertThat(output).has(substring(1, "---->"));
 | 
			
		||||
		assertThat(output).has(substring(1, "----> josh"));
 | 
			
		||||
		assertThat(output).has(substring(2, "Count is 1"));
 | 
			
		||||
		assertThat(output).has(substring(1, "Simulated error"));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private Condition<String> substring(final int times, final String substring) {
 | 
			
		||||
		return new Condition<String>(
 | 
			
		||||
				"containing '" + substring + "' " + times + " times") {
 | 
			
		||||
 | 
			
		||||
			@Override
 | 
			
		||||
			public boolean matches(String value) {
 | 
			
		||||
				int i = 0;
 | 
			
		||||
				while (value.contains(substring)) {
 | 
			
		||||
					int beginIndex = value.indexOf(substring) + substring.length();
 | 
			
		||||
					value = value.substring(beginIndex);
 | 
			
		||||
					i++;
 | 
			
		||||
				}
 | 
			
		||||
				return i == times;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -48,6 +48,7 @@
 | 
			
		|||
		<module>spring-boot-starter-jooq</module>
 | 
			
		||||
		<module>spring-boot-starter-jta-atomikos</module>
 | 
			
		||||
		<module>spring-boot-starter-jta-bitronix</module>
 | 
			
		||||
		<module>spring-boot-starter-jta-narayana</module>
 | 
			
		||||
		<module>spring-boot-starter-logging</module>
 | 
			
		||||
		<module>spring-boot-starter-log4j2</module>
 | 
			
		||||
		<module>spring-boot-starter-mail</module>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,47 @@
 | 
			
		|||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 | 
			
		||||
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 | 
			
		||||
	<modelVersion>4.0.0</modelVersion>
 | 
			
		||||
	<parent>
 | 
			
		||||
		<artifactId>spring-boot-starters</artifactId>
 | 
			
		||||
		<groupId>org.springframework.boot</groupId>
 | 
			
		||||
		<version>1.4.0.BUILD-SNAPSHOT</version>
 | 
			
		||||
	</parent>
 | 
			
		||||
	<artifactId>spring-boot-starter-jta-narayana</artifactId>
 | 
			
		||||
	<name>Spring Boot Narayana JTA Starter</name>
 | 
			
		||||
	<description>Spring Boot Narayana JTA Starter</description>
 | 
			
		||||
	<url>http://projects.spring.io/spring-boot/</url>
 | 
			
		||||
	<properties>
 | 
			
		||||
		<main.basedir>${basedir}/../..</main.basedir>
 | 
			
		||||
	</properties>
 | 
			
		||||
	<dependencies>
 | 
			
		||||
		<dependency>
 | 
			
		||||
			<groupId>org.springframework.boot</groupId>
 | 
			
		||||
			<artifactId>spring-boot-starter</artifactId>
 | 
			
		||||
		</dependency>
 | 
			
		||||
		<dependency>
 | 
			
		||||
			<groupId>org.jboss</groupId>
 | 
			
		||||
			<artifactId>jboss-transaction-spi</artifactId>
 | 
			
		||||
		</dependency>
 | 
			
		||||
		<dependency>
 | 
			
		||||
			<groupId>org.jboss.narayana.jta</groupId>
 | 
			
		||||
			<artifactId>jdbc</artifactId>
 | 
			
		||||
		</dependency>
 | 
			
		||||
		<dependency>
 | 
			
		||||
			<groupId>org.jboss.narayana.jta</groupId>
 | 
			
		||||
			<artifactId>jms</artifactId>
 | 
			
		||||
		</dependency>
 | 
			
		||||
		<dependency>
 | 
			
		||||
			<groupId>org.jboss.narayana.jta</groupId>
 | 
			
		||||
			<artifactId>jta</artifactId>
 | 
			
		||||
		</dependency>
 | 
			
		||||
		<dependency>
 | 
			
		||||
			<groupId>org.jboss.narayana.jts</groupId>
 | 
			
		||||
			<artifactId>narayana-jts-integration</artifactId>
 | 
			
		||||
		</dependency>
 | 
			
		||||
		<dependency>
 | 
			
		||||
			<groupId>javax.transaction</groupId>
 | 
			
		||||
			<artifactId>javax.transaction-api</artifactId>
 | 
			
		||||
		</dependency>
 | 
			
		||||
	</dependencies>
 | 
			
		||||
</project>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
provides: jta, jdbc, jms, jboss-transaction-spi
 | 
			
		||||
| 
						 | 
				
			
			@ -229,6 +229,31 @@
 | 
			
		|||
			<artifactId>snakeyaml</artifactId>
 | 
			
		||||
			<optional>true</optional>
 | 
			
		||||
		</dependency>
 | 
			
		||||
		<dependency>
 | 
			
		||||
			<groupId>org.jboss.narayana.jta</groupId>
 | 
			
		||||
			<artifactId>jta</artifactId>
 | 
			
		||||
			<optional>true</optional>
 | 
			
		||||
		</dependency>
 | 
			
		||||
		<dependency>
 | 
			
		||||
			<groupId>org.jboss.narayana.jta</groupId>
 | 
			
		||||
			<artifactId>jdbc</artifactId>
 | 
			
		||||
			<optional>true</optional>
 | 
			
		||||
		</dependency>
 | 
			
		||||
		<dependency>
 | 
			
		||||
			<groupId>org.jboss.narayana.jta</groupId>
 | 
			
		||||
			<artifactId>jms</artifactId>
 | 
			
		||||
			<optional>true</optional>
 | 
			
		||||
		</dependency>
 | 
			
		||||
		<dependency>
 | 
			
		||||
			<groupId>org.jboss.narayana.jts</groupId>
 | 
			
		||||
			<artifactId>narayana-jts-integration</artifactId>
 | 
			
		||||
			<optional>true</optional>
 | 
			
		||||
		</dependency>
 | 
			
		||||
		<dependency>
 | 
			
		||||
			<groupId>org.jboss</groupId>
 | 
			
		||||
			<artifactId>jboss-transaction-spi</artifactId>
 | 
			
		||||
			<optional>true</optional>
 | 
			
		||||
		</dependency>
 | 
			
		||||
		<!-- Annotation processing -->
 | 
			
		||||
		<dependency>
 | 
			
		||||
			<groupId>org.springframework.boot</groupId>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,190 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2016 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.springframework.boot.jta.narayana;
 | 
			
		||||
 | 
			
		||||
import java.sql.SQLException;
 | 
			
		||||
 | 
			
		||||
import javax.sql.XAConnection;
 | 
			
		||||
import javax.sql.XADataSource;
 | 
			
		||||
import javax.transaction.xa.XAException;
 | 
			
		||||
import javax.transaction.xa.XAResource;
 | 
			
		||||
import javax.transaction.xa.Xid;
 | 
			
		||||
 | 
			
		||||
import com.arjuna.ats.jta.recovery.XAResourceRecoveryHelper;
 | 
			
		||||
import org.apache.commons.logging.Log;
 | 
			
		||||
import org.apache.commons.logging.LogFactory;
 | 
			
		||||
 | 
			
		||||
import org.springframework.util.Assert;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * XAResourceRecoveryHelper implementation which gets XIDs, which needs to be recovered,
 | 
			
		||||
 * from the database.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Gytis Trikleris
 | 
			
		||||
 * @since 1.4.0
 | 
			
		||||
 */
 | 
			
		||||
public class DataSourceXAResourceRecoveryHelper
 | 
			
		||||
		implements XAResourceRecoveryHelper, XAResource {
 | 
			
		||||
 | 
			
		||||
	private static final XAResource[] NO_XA_RESOURCES = {};
 | 
			
		||||
 | 
			
		||||
	private static final Log logger = LogFactory
 | 
			
		||||
			.getLog(DataSourceXAResourceRecoveryHelper.class);
 | 
			
		||||
 | 
			
		||||
	private final XADataSource xaDataSource;
 | 
			
		||||
 | 
			
		||||
	private final String user;
 | 
			
		||||
 | 
			
		||||
	private final String password;
 | 
			
		||||
 | 
			
		||||
	private XAConnection xaConnection;
 | 
			
		||||
 | 
			
		||||
	private XAResource delegate;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Create a new {@link DataSourceXAResourceRecoveryHelper} instance.
 | 
			
		||||
	 * @param xaDataSource the XA data source
 | 
			
		||||
	 */
 | 
			
		||||
	public DataSourceXAResourceRecoveryHelper(XADataSource xaDataSource) {
 | 
			
		||||
		this(xaDataSource, null, null);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Create a new {@link DataSourceXAResourceRecoveryHelper} instance.
 | 
			
		||||
	 * @param xaDataSource the XA data source
 | 
			
		||||
	 * @param user the database user or {@code null}
 | 
			
		||||
	 * @param password the database password or {@code null}
 | 
			
		||||
	 */
 | 
			
		||||
	public DataSourceXAResourceRecoveryHelper(XADataSource xaDataSource, String user,
 | 
			
		||||
			String password) {
 | 
			
		||||
		Assert.notNull(xaDataSource, "XADataSource must not be null");
 | 
			
		||||
		this.xaDataSource = xaDataSource;
 | 
			
		||||
		this.user = user;
 | 
			
		||||
		this.password = password;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public boolean initialise(String properties) {
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public XAResource[] getXAResources() {
 | 
			
		||||
		if (connect()) {
 | 
			
		||||
			return new XAResource[] { this };
 | 
			
		||||
		}
 | 
			
		||||
		return NO_XA_RESOURCES;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private boolean connect() {
 | 
			
		||||
		if (this.delegate == null) {
 | 
			
		||||
			try {
 | 
			
		||||
				this.xaConnection = getXaConnection();
 | 
			
		||||
				this.delegate = this.xaConnection.getXAResource();
 | 
			
		||||
			}
 | 
			
		||||
			catch (SQLException ex) {
 | 
			
		||||
				logger.warn("Failed to create connection", ex);
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private XAConnection getXaConnection() throws SQLException {
 | 
			
		||||
		if (this.user == null && this.password == null) {
 | 
			
		||||
			return this.xaDataSource.getXAConnection();
 | 
			
		||||
		}
 | 
			
		||||
		return this.xaDataSource.getXAConnection(this.user, this.password);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Xid[] recover(int flag) throws XAException {
 | 
			
		||||
		try {
 | 
			
		||||
			return getDelegate(true).recover(flag);
 | 
			
		||||
		}
 | 
			
		||||
		finally {
 | 
			
		||||
			if (flag == XAResource.TMENDRSCAN) {
 | 
			
		||||
				disconnect();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void disconnect() throws XAException {
 | 
			
		||||
		try {
 | 
			
		||||
			this.xaConnection.close();
 | 
			
		||||
		}
 | 
			
		||||
		catch (SQLException e) {
 | 
			
		||||
			logger.warn("Failed to close connection", e);
 | 
			
		||||
		}
 | 
			
		||||
		finally {
 | 
			
		||||
			this.xaConnection = null;
 | 
			
		||||
			this.delegate = null;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void start(Xid xid, int flags) throws XAException {
 | 
			
		||||
		getDelegate(true).start(xid, flags);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void end(Xid xid, int flags) throws XAException {
 | 
			
		||||
		getDelegate(true).end(xid, flags);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public int prepare(Xid xid) throws XAException {
 | 
			
		||||
		return getDelegate(true).prepare(xid);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void commit(Xid xid, boolean onePhase) throws XAException {
 | 
			
		||||
		getDelegate(true).commit(xid, onePhase);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void rollback(Xid xid) throws XAException {
 | 
			
		||||
		getDelegate(true).rollback(xid);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public boolean isSameRM(XAResource xaResource) throws XAException {
 | 
			
		||||
		return getDelegate(true).isSameRM(xaResource);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void forget(Xid xid) throws XAException {
 | 
			
		||||
		getDelegate(true).forget(xid);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public int getTransactionTimeout() throws XAException {
 | 
			
		||||
		return getDelegate(true).getTransactionTimeout();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public boolean setTransactionTimeout(int seconds) throws XAException {
 | 
			
		||||
		return getDelegate(true).setTransactionTimeout(seconds);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private XAResource getDelegate(boolean required) {
 | 
			
		||||
		Assert.state(this.delegate != null || !required,
 | 
			
		||||
				"Connection has not been opened");
 | 
			
		||||
		return this.delegate;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,87 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2016 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.springframework.boot.jta.narayana;
 | 
			
		||||
 | 
			
		||||
import javax.transaction.TransactionManager;
 | 
			
		||||
 | 
			
		||||
import org.springframework.beans.BeansException;
 | 
			
		||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
 | 
			
		||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
 | 
			
		||||
import org.springframework.core.Ordered;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * {@link BeanFactoryPostProcessor} to automatically setup correct beans ordering.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Gytis Trikleris
 | 
			
		||||
 * @since 1.4.0
 | 
			
		||||
 */
 | 
			
		||||
public class NarayanaBeanFactoryPostProcessor
 | 
			
		||||
		implements BeanFactoryPostProcessor, Ordered {
 | 
			
		||||
 | 
			
		||||
	private static final String[] NO_BEANS = {};
 | 
			
		||||
 | 
			
		||||
	private static final int ORDER = Ordered.LOWEST_PRECEDENCE;
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
 | 
			
		||||
			throws BeansException {
 | 
			
		||||
		String[] transactionManagers = beanFactory
 | 
			
		||||
				.getBeanNamesForType(TransactionManager.class, true, false);
 | 
			
		||||
		String[] recoveryManagers = beanFactory
 | 
			
		||||
				.getBeanNamesForType(NarayanaRecoveryManagerBean.class, true, false);
 | 
			
		||||
		addBeanDependencies(beanFactory, transactionManagers, "javax.sql.DataSource");
 | 
			
		||||
		addBeanDependencies(beanFactory, recoveryManagers, "javax.sql.DataSource");
 | 
			
		||||
		addBeanDependencies(beanFactory, transactionManagers,
 | 
			
		||||
				"javax.jms.ConnectionFactory");
 | 
			
		||||
		addBeanDependencies(beanFactory, recoveryManagers, "javax.jms.ConnectionFactory");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void addBeanDependencies(ConfigurableListableBeanFactory beanFactory,
 | 
			
		||||
			String[] beanNames, String dependencyType) {
 | 
			
		||||
		for (String beanName : beanNames) {
 | 
			
		||||
			addBeanDependencies(beanFactory, beanName, dependencyType);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void addBeanDependencies(ConfigurableListableBeanFactory beanFactory,
 | 
			
		||||
			String beanName, String dependencyType) {
 | 
			
		||||
		for (String dependentBeanName : getBeanNamesForType(beanFactory,
 | 
			
		||||
				dependencyType)) {
 | 
			
		||||
			beanFactory.registerDependentBean(beanName, dependentBeanName);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private String[] getBeanNamesForType(ConfigurableListableBeanFactory beanFactory,
 | 
			
		||||
			String type) {
 | 
			
		||||
		try {
 | 
			
		||||
			return beanFactory.getBeanNamesForType(Class.forName(type), true, false);
 | 
			
		||||
		}
 | 
			
		||||
		catch (ClassNotFoundException ex) {
 | 
			
		||||
			// Ignore
 | 
			
		||||
		}
 | 
			
		||||
		catch (NoClassDefFoundError ex) {
 | 
			
		||||
			// Ignore
 | 
			
		||||
		}
 | 
			
		||||
		return NO_BEANS;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public int getOrder() {
 | 
			
		||||
		return ORDER;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,123 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2016 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.springframework.boot.jta.narayana;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import com.arjuna.ats.arjuna.common.CoordinatorEnvironmentBean;
 | 
			
		||||
import com.arjuna.ats.arjuna.common.CoreEnvironmentBean;
 | 
			
		||||
import com.arjuna.ats.arjuna.common.CoreEnvironmentBeanException;
 | 
			
		||||
import com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean;
 | 
			
		||||
import com.arjuna.ats.arjuna.common.RecoveryEnvironmentBean;
 | 
			
		||||
import com.arjuna.ats.jta.common.JTAEnvironmentBean;
 | 
			
		||||
import com.arjuna.common.internal.util.propertyservice.BeanPopulator;
 | 
			
		||||
 | 
			
		||||
import org.springframework.beans.factory.InitializingBean;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Bean that configures Narayana transaction manager.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Gytis Trikleris
 | 
			
		||||
 * @since 1.4.0
 | 
			
		||||
 */
 | 
			
		||||
public class NarayanaConfigurationBean implements InitializingBean {
 | 
			
		||||
 | 
			
		||||
	private static final String JBOSSTS_PROPERTIES_FILE_NAME = "jbossts-properties.xml";
 | 
			
		||||
 | 
			
		||||
	private final NarayanaProperties properties;
 | 
			
		||||
 | 
			
		||||
	public NarayanaConfigurationBean(NarayanaProperties narayanaProperties) {
 | 
			
		||||
		this.properties = narayanaProperties;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void afterPropertiesSet() throws Exception {
 | 
			
		||||
		if (isPropertiesFileAvailable()) {
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		setNodeIdentifier(this.properties.getTransactionManagerId());
 | 
			
		||||
		setObjectStoreDir(this.properties.getLogDir());
 | 
			
		||||
		setCommitOnePhase(this.properties.isOnePhaseCommit());
 | 
			
		||||
		setDefaultTimeout(this.properties.getDefaultTimeout());
 | 
			
		||||
		setPeriodicRecoveryPeriod(this.properties.getPeriodicRecoveryPeriod());
 | 
			
		||||
		setRecoveryBackoffPeriod(this.properties.getRecoveryBackoffPeriod());
 | 
			
		||||
		setXaResourceOrphanFilters(this.properties.getXaResourceOrphanFilters());
 | 
			
		||||
		setRecoveryModules(this.properties.getRecoveryModules());
 | 
			
		||||
		setExpiryScanners(this.properties.getExpiryScanners());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private boolean isPropertiesFileAvailable() {
 | 
			
		||||
		return Thread.currentThread().getContextClassLoader()
 | 
			
		||||
				.getResource(JBOSSTS_PROPERTIES_FILE_NAME) != null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void setNodeIdentifier(String nodeIdentifier)
 | 
			
		||||
			throws CoreEnvironmentBeanException {
 | 
			
		||||
		getPopulator(CoreEnvironmentBean.class).setNodeIdentifier(nodeIdentifier);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void setObjectStoreDir(String objectStoreDir) {
 | 
			
		||||
		getPopulator(ObjectStoreEnvironmentBean.class).setObjectStoreDir(objectStoreDir);
 | 
			
		||||
		getPopulator(ObjectStoreEnvironmentBean.class, "communicationStore")
 | 
			
		||||
				.setObjectStoreDir(objectStoreDir);
 | 
			
		||||
		getPopulator(ObjectStoreEnvironmentBean.class, "stateStore")
 | 
			
		||||
				.setObjectStoreDir(objectStoreDir);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void setCommitOnePhase(boolean isCommitOnePhase) {
 | 
			
		||||
		getPopulator(CoordinatorEnvironmentBean.class)
 | 
			
		||||
				.setCommitOnePhase(isCommitOnePhase);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void setDefaultTimeout(int defaultTimeout) {
 | 
			
		||||
		getPopulator(CoordinatorEnvironmentBean.class).setDefaultTimeout(defaultTimeout);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void setPeriodicRecoveryPeriod(int periodicRecoveryPeriod) {
 | 
			
		||||
		getPopulator(RecoveryEnvironmentBean.class)
 | 
			
		||||
				.setPeriodicRecoveryPeriod(periodicRecoveryPeriod);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void setRecoveryBackoffPeriod(int recoveryBackoffPeriod) {
 | 
			
		||||
		getPopulator(RecoveryEnvironmentBean.class)
 | 
			
		||||
				.setRecoveryBackoffPeriod(recoveryBackoffPeriod);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void setXaResourceOrphanFilters(List<String> xaResourceOrphanFilters) {
 | 
			
		||||
		getPopulator(JTAEnvironmentBean.class)
 | 
			
		||||
				.setXaResourceOrphanFilterClassNames(xaResourceOrphanFilters);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void setRecoveryModules(List<String> recoveryModules) {
 | 
			
		||||
		getPopulator(RecoveryEnvironmentBean.class)
 | 
			
		||||
				.setRecoveryModuleClassNames(recoveryModules);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void setExpiryScanners(List<String> expiryScanners) {
 | 
			
		||||
		getPopulator(RecoveryEnvironmentBean.class)
 | 
			
		||||
				.setExpiryScannerClassNames(expiryScanners);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private <T> T getPopulator(Class<T> beanClass) {
 | 
			
		||||
		return BeanPopulator.getDefaultInstance(beanClass);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private <T> T getPopulator(Class<T> beanClass, String name) {
 | 
			
		||||
		return BeanPopulator.getNamedInstance(beanClass, name);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,114 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2016 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.springframework.boot.jta.narayana;
 | 
			
		||||
 | 
			
		||||
import java.io.PrintWriter;
 | 
			
		||||
import java.sql.Connection;
 | 
			
		||||
import java.sql.SQLException;
 | 
			
		||||
import java.sql.SQLFeatureNotSupportedException;
 | 
			
		||||
import java.util.Properties;
 | 
			
		||||
import java.util.logging.Logger;
 | 
			
		||||
 | 
			
		||||
import javax.sql.DataSource;
 | 
			
		||||
import javax.sql.XADataSource;
 | 
			
		||||
 | 
			
		||||
import com.arjuna.ats.internal.jdbc.ConnectionManager;
 | 
			
		||||
import com.arjuna.ats.jdbc.TransactionalDriver;
 | 
			
		||||
 | 
			
		||||
import org.springframework.util.Assert;
 | 
			
		||||
import org.springframework.util.ClassUtils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * {@link DataSource} implementation wrapping {@link XADataSource} and using
 | 
			
		||||
 * {@link ConnectionManager} to acquire connections.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Gytis Trikleris
 | 
			
		||||
 * @since 1.4.0
 | 
			
		||||
 */
 | 
			
		||||
public class NarayanaDataSourceBean implements DataSource {
 | 
			
		||||
 | 
			
		||||
	private final XADataSource xaDataSource;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Create a new {@link NarayanaDataSourceBean} instance.
 | 
			
		||||
	 * @param xaDataSource the XA DataSource
 | 
			
		||||
	 */
 | 
			
		||||
	public NarayanaDataSourceBean(XADataSource xaDataSource) {
 | 
			
		||||
		Assert.notNull(xaDataSource, "XADataSource must not be null");
 | 
			
		||||
		this.xaDataSource = xaDataSource;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Connection getConnection() throws SQLException {
 | 
			
		||||
		Properties properties = new Properties();
 | 
			
		||||
		properties.put(TransactionalDriver.XADataSource, this.xaDataSource);
 | 
			
		||||
		return ConnectionManager.create(null, properties);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Connection getConnection(String username, String password)
 | 
			
		||||
			throws SQLException {
 | 
			
		||||
		Properties properties = new Properties();
 | 
			
		||||
		properties.put(TransactionalDriver.XADataSource, this.xaDataSource);
 | 
			
		||||
		properties.put(TransactionalDriver.userName, username);
 | 
			
		||||
		properties.put(TransactionalDriver.password, password);
 | 
			
		||||
		return ConnectionManager.create(null, properties);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public PrintWriter getLogWriter() throws SQLException {
 | 
			
		||||
		return this.xaDataSource.getLogWriter();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void setLogWriter(PrintWriter out) throws SQLException {
 | 
			
		||||
		this.xaDataSource.setLogWriter(out);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void setLoginTimeout(int seconds) throws SQLException {
 | 
			
		||||
		this.xaDataSource.setLoginTimeout(seconds);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public int getLoginTimeout() throws SQLException {
 | 
			
		||||
		return this.xaDataSource.getLoginTimeout();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Logger getParentLogger() throws SQLFeatureNotSupportedException {
 | 
			
		||||
		throw new SQLFeatureNotSupportedException();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@SuppressWarnings("unchecked")
 | 
			
		||||
	@Override
 | 
			
		||||
	public <T> T unwrap(Class<T> iface) throws SQLException {
 | 
			
		||||
		if (isWrapperFor(iface)) {
 | 
			
		||||
			return (T) this;
 | 
			
		||||
		}
 | 
			
		||||
		if (ClassUtils.isAssignableValue(iface, this.xaDataSource)) {
 | 
			
		||||
			return (T) this.xaDataSource;
 | 
			
		||||
		}
 | 
			
		||||
		throw new SQLException(getClass() + " is not a wrapper for " + iface);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public boolean isWrapperFor(Class<?> iface) throws SQLException {
 | 
			
		||||
		return iface.isAssignableFrom(getClass());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,230 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2016 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.springframework.boot.jta.narayana;
 | 
			
		||||
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import org.springframework.boot.context.properties.ConfigurationProperties;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Subset of Narayana properties which can be configured via Spring configuration. Use
 | 
			
		||||
 * jbossts-properties.xml for complete configuration.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Gytis Trikleris
 | 
			
		||||
 * @since 1.4.0
 | 
			
		||||
 */
 | 
			
		||||
@ConfigurationProperties(prefix = NarayanaProperties.PROPERTIES_PREFIX)
 | 
			
		||||
public class NarayanaProperties {
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Prefix for Narayana specific properties.
 | 
			
		||||
	 */
 | 
			
		||||
	public static final String PROPERTIES_PREFIX = "spring.jta.narayana";
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Transaction object store directory. Default: target/tx-object-store.
 | 
			
		||||
	 */
 | 
			
		||||
	private String logDir = "target/tx-object-store";
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Unique transaction manager id. Default: 1.
 | 
			
		||||
	 */
 | 
			
		||||
	private String transactionManagerId = "1";
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Enable one phase commit optimisation. Default: <code>true</code>.
 | 
			
		||||
	 */
 | 
			
		||||
	private boolean onePhaseCommit = true;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Transaction timeout in seconds. Default: <code>60</code>.
 | 
			
		||||
	 */
 | 
			
		||||
	private int defaultTimeout = 60;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Interval in which periodic recovery scans are performed in seconds. Default:
 | 
			
		||||
	 * <code>120</code>
 | 
			
		||||
	 */
 | 
			
		||||
	private int periodicRecoveryPeriod = 120;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Back off period between first and second phases of the recovery scan in seconds.
 | 
			
		||||
	 * Default: <code>10</code>
 | 
			
		||||
	 */
 | 
			
		||||
	private int recoveryBackoffPeriod = 10;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Database username to be used by recovery manager. Default: <code>null</code>
 | 
			
		||||
	 */
 | 
			
		||||
	private String recoveryDbUser = null;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Database password to be used by recovery manager. Default: <code>null</code>
 | 
			
		||||
	 */
 | 
			
		||||
	private String recoveryDbPass = null;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * JMS username to be used by recovery manager. Default: <code>null</code>
 | 
			
		||||
	 */
 | 
			
		||||
	private String recoveryJmsUser = null;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * JMS password to be used by recovery manager. Default: <code>null</code>
 | 
			
		||||
	 */
 | 
			
		||||
	private String recoveryJmsPass = null;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * List of orphan filters. Default:
 | 
			
		||||
	 * <ul>
 | 
			
		||||
	 * <li>com.arjuna.ats.internal.jta.recovery.arjunacore.
 | 
			
		||||
	 * JTATransactionLogXAResourceOrphanFilter</li>
 | 
			
		||||
	 * <li>
 | 
			
		||||
	 * com.arjuna.ats.internal.jta.recovery.arjunacore.JTANodeNameXAResourceOrphanFilter
 | 
			
		||||
	 * </li>
 | 
			
		||||
	 * </ul>
 | 
			
		||||
	 */
 | 
			
		||||
	private List<String> xaResourceOrphanFilters = Arrays.asList(
 | 
			
		||||
			"com.arjuna.ats.internal.jta.recovery.arjunacore.JTATransactionLogXAResourceOrphanFilter",
 | 
			
		||||
			"com.arjuna.ats.internal.jta.recovery.arjunacore.JTANodeNameXAResourceOrphanFilter");
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * List of recovery modules. Default:
 | 
			
		||||
	 * <ul>
 | 
			
		||||
	 * <li>com.arjuna.ats.internal.arjuna.recovery.AtomicActionRecoveryModule</li>
 | 
			
		||||
	 * <li>com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule</li>
 | 
			
		||||
	 * </ul>
 | 
			
		||||
	 */
 | 
			
		||||
	private List<String> recoveryModules = Arrays.asList(
 | 
			
		||||
			"com.arjuna.ats.internal.arjuna.recovery.AtomicActionRecoveryModule",
 | 
			
		||||
			"com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule");
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * List of expiry scanners. Default:
 | 
			
		||||
	 * <ul>
 | 
			
		||||
	 * <li>com.arjuna.ats.internal.arjuna.recovery.ExpiredTransactionStatusManagerScanner
 | 
			
		||||
	 * </li>
 | 
			
		||||
	 * </ul>
 | 
			
		||||
	 */
 | 
			
		||||
	private List<String> expiryScanners = Arrays.asList(
 | 
			
		||||
			"com.arjuna.ats.internal.arjuna.recovery.ExpiredTransactionStatusManagerScanner");
 | 
			
		||||
 | 
			
		||||
	public String getLogDir() {
 | 
			
		||||
		return this.logDir;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void setLogDir(String logDir) {
 | 
			
		||||
		this.logDir = logDir;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public String getTransactionManagerId() {
 | 
			
		||||
		return this.transactionManagerId;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void setTransactionManagerId(String transactionManagerId) {
 | 
			
		||||
		this.transactionManagerId = transactionManagerId;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public boolean isOnePhaseCommit() {
 | 
			
		||||
		return this.onePhaseCommit;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void setOnePhaseCommit(boolean onePhaseCommit) {
 | 
			
		||||
		this.onePhaseCommit = onePhaseCommit;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public int getDefaultTimeout() {
 | 
			
		||||
		return this.defaultTimeout;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public int getPeriodicRecoveryPeriod() {
 | 
			
		||||
		return this.periodicRecoveryPeriod;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void setPeriodicRecoveryPeriod(int periodicRecoveryPeriod) {
 | 
			
		||||
		this.periodicRecoveryPeriod = periodicRecoveryPeriod;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public int getRecoveryBackoffPeriod() {
 | 
			
		||||
		return this.recoveryBackoffPeriod;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void setRecoveryBackoffPeriod(int recoveryBackoffPeriod) {
 | 
			
		||||
		this.recoveryBackoffPeriod = recoveryBackoffPeriod;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void setDefaultTimeout(int defaultTimeout) {
 | 
			
		||||
		this.defaultTimeout = defaultTimeout;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public List<String> getXaResourceOrphanFilters() {
 | 
			
		||||
		return this.xaResourceOrphanFilters;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void setXaResourceOrphanFilters(List<String> xaResourceOrphanFilters) {
 | 
			
		||||
		this.xaResourceOrphanFilters = xaResourceOrphanFilters;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public List<String> getRecoveryModules() {
 | 
			
		||||
		return this.recoveryModules;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void setRecoveryModules(List<String> recoveryModules) {
 | 
			
		||||
		this.recoveryModules = recoveryModules;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public List<String> getExpiryScanners() {
 | 
			
		||||
		return this.expiryScanners;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void setExpiryScanners(List<String> expiryScanners) {
 | 
			
		||||
		this.expiryScanners = expiryScanners;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public String getRecoveryDbUser() {
 | 
			
		||||
		return this.recoveryDbUser;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void setRecoveryDbUser(String recoveryDbUser) {
 | 
			
		||||
		this.recoveryDbUser = recoveryDbUser;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public String getRecoveryDbPass() {
 | 
			
		||||
		return this.recoveryDbPass;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void setRecoveryDbPass(String recoveryDbPass) {
 | 
			
		||||
		this.recoveryDbPass = recoveryDbPass;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public String getRecoveryJmsUser() {
 | 
			
		||||
		return this.recoveryJmsUser;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void setRecoveryJmsUser(String recoveryJmsUser) {
 | 
			
		||||
		this.recoveryJmsUser = recoveryJmsUser;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public String getRecoveryJmsPass() {
 | 
			
		||||
		return this.recoveryJmsPass;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void setRecoveryJmsPass(String recoveryJmsPass) {
 | 
			
		||||
		this.recoveryJmsPass = recoveryJmsPass;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,72 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2016 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.springframework.boot.jta.narayana;
 | 
			
		||||
 | 
			
		||||
import com.arjuna.ats.arjuna.recovery.RecoveryManager;
 | 
			
		||||
import com.arjuna.ats.arjuna.recovery.RecoveryModule;
 | 
			
		||||
import com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule;
 | 
			
		||||
import com.arjuna.ats.jbossatx.jta.RecoveryManagerService;
 | 
			
		||||
import com.arjuna.ats.jta.recovery.XAResourceRecoveryHelper;
 | 
			
		||||
 | 
			
		||||
import org.springframework.beans.factory.DisposableBean;
 | 
			
		||||
import org.springframework.beans.factory.InitializingBean;
 | 
			
		||||
import org.springframework.util.Assert;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Bean to set up Narayana recovery manager.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Gytis Trikleris
 | 
			
		||||
 * @since 1.4.0
 | 
			
		||||
 */
 | 
			
		||||
public class NarayanaRecoveryManagerBean implements InitializingBean, DisposableBean {
 | 
			
		||||
 | 
			
		||||
	private final RecoveryManagerService recoveryManagerService;
 | 
			
		||||
 | 
			
		||||
	public NarayanaRecoveryManagerBean(RecoveryManagerService recoveryManagerService) {
 | 
			
		||||
		Assert.notNull(recoveryManagerService, "RecoveryManagerService must not be null");
 | 
			
		||||
		this.recoveryManagerService = recoveryManagerService;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void afterPropertiesSet() throws Exception {
 | 
			
		||||
		this.recoveryManagerService.create();
 | 
			
		||||
		this.recoveryManagerService.start();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void destroy() throws Exception {
 | 
			
		||||
		this.recoveryManagerService.stop();
 | 
			
		||||
		this.recoveryManagerService.destroy();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void registerXAResourceRecoveryHelper(
 | 
			
		||||
			XAResourceRecoveryHelper xaResourceRecoveryHelper) {
 | 
			
		||||
		getXARecoveryModule(RecoveryManager.manager())
 | 
			
		||||
				.addXAResourceRecoveryHelper(xaResourceRecoveryHelper);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private XARecoveryModule getXARecoveryModule(RecoveryManager recoveryManager) {
 | 
			
		||||
		for (RecoveryModule recoveryModule : recoveryManager.getModules()) {
 | 
			
		||||
			if (recoveryModule instanceof XARecoveryModule) {
 | 
			
		||||
				return (XARecoveryModule) recoveryModule;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		throw new IllegalStateException(
 | 
			
		||||
				"XARecoveryModule is not registered with recovery manager");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,82 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2016 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.springframework.boot.jta.narayana;
 | 
			
		||||
 | 
			
		||||
import javax.jms.ConnectionFactory;
 | 
			
		||||
import javax.jms.XAConnectionFactory;
 | 
			
		||||
import javax.transaction.TransactionManager;
 | 
			
		||||
 | 
			
		||||
import com.arjuna.ats.jta.recovery.XAResourceRecoveryHelper;
 | 
			
		||||
import org.jboss.narayana.jta.jms.ConnectionFactoryProxy;
 | 
			
		||||
import org.jboss.narayana.jta.jms.JmsXAResourceRecoveryHelper;
 | 
			
		||||
import org.jboss.narayana.jta.jms.TransactionHelperImpl;
 | 
			
		||||
 | 
			
		||||
import org.springframework.boot.jta.XAConnectionFactoryWrapper;
 | 
			
		||||
import org.springframework.util.Assert;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * {@link XAConnectionFactoryWrapper} that uses {@link ConnectionFactoryProxy} to wrap an
 | 
			
		||||
 * {@link XAConnectionFactory}.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Gytis Trikleris
 | 
			
		||||
 * @since 1.4.0
 | 
			
		||||
 */
 | 
			
		||||
public class NarayanaXAConnectionFactoryWrapper implements XAConnectionFactoryWrapper {
 | 
			
		||||
 | 
			
		||||
	private final TransactionManager transactionManager;
 | 
			
		||||
 | 
			
		||||
	private final NarayanaRecoveryManagerBean recoveryManager;
 | 
			
		||||
 | 
			
		||||
	private final NarayanaProperties properties;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Create a new {@link NarayanaXAConnectionFactoryWrapper} instance.
 | 
			
		||||
	 * @param transactionManager the underlying transaction manager
 | 
			
		||||
	 * @param recoveryManager the underlying recovery manager
 | 
			
		||||
	 * @param properties the Narayana properties
 | 
			
		||||
	 */
 | 
			
		||||
	public NarayanaXAConnectionFactoryWrapper(TransactionManager transactionManager,
 | 
			
		||||
			NarayanaRecoveryManagerBean recoveryManager, NarayanaProperties properties) {
 | 
			
		||||
		Assert.notNull(transactionManager, "TransactionManager must not be null");
 | 
			
		||||
		Assert.notNull(recoveryManager, "RecoveryManager must not be null");
 | 
			
		||||
		Assert.notNull(properties, "Properties must not be null");
 | 
			
		||||
		this.transactionManager = transactionManager;
 | 
			
		||||
		this.recoveryManager = recoveryManager;
 | 
			
		||||
		this.properties = properties;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public ConnectionFactory wrapConnectionFactory(
 | 
			
		||||
			XAConnectionFactory xaConnectionFactory) {
 | 
			
		||||
		XAResourceRecoveryHelper recoveryHelper = getRecoveryHelper(xaConnectionFactory);
 | 
			
		||||
		this.recoveryManager.registerXAResourceRecoveryHelper(recoveryHelper);
 | 
			
		||||
		return new ConnectionFactoryProxy(xaConnectionFactory,
 | 
			
		||||
				new TransactionHelperImpl(this.transactionManager));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private XAResourceRecoveryHelper getRecoveryHelper(
 | 
			
		||||
			XAConnectionFactory xaConnectionFactory) {
 | 
			
		||||
		if (this.properties.getRecoveryJmsUser() == null
 | 
			
		||||
				&& this.properties.getRecoveryJmsPass() == null) {
 | 
			
		||||
			return new JmsXAResourceRecoveryHelper(xaConnectionFactory);
 | 
			
		||||
		}
 | 
			
		||||
		return new JmsXAResourceRecoveryHelper(xaConnectionFactory,
 | 
			
		||||
				this.properties.getRecoveryJmsUser(),
 | 
			
		||||
				this.properties.getRecoveryJmsPass());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,69 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2016 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.springframework.boot.jta.narayana;
 | 
			
		||||
 | 
			
		||||
import javax.sql.DataSource;
 | 
			
		||||
import javax.sql.XADataSource;
 | 
			
		||||
 | 
			
		||||
import com.arjuna.ats.jta.recovery.XAResourceRecoveryHelper;
 | 
			
		||||
 | 
			
		||||
import org.springframework.boot.jta.XADataSourceWrapper;
 | 
			
		||||
import org.springframework.util.Assert;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * {@link XADataSourceWrapper} that uses {@link NarayanaDataSourceBean} to wrap an
 | 
			
		||||
 * {@link XADataSource}.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Gytis Trikleris
 | 
			
		||||
 * @since 1.4.0
 | 
			
		||||
 */
 | 
			
		||||
public class NarayanaXADataSourceWrapper implements XADataSourceWrapper {
 | 
			
		||||
 | 
			
		||||
	private final NarayanaRecoveryManagerBean recoveryManager;
 | 
			
		||||
 | 
			
		||||
	private final NarayanaProperties properties;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Create a new {@link NarayanaXADataSourceWrapper} instance.
 | 
			
		||||
	 * @param recoveryManager the underlying recovery manager
 | 
			
		||||
	 * @param properties the Narayana properties
 | 
			
		||||
	 */
 | 
			
		||||
	public NarayanaXADataSourceWrapper(NarayanaRecoveryManagerBean recoveryManager,
 | 
			
		||||
			NarayanaProperties properties) {
 | 
			
		||||
		Assert.notNull(recoveryManager, "RecoveryManager must not be null");
 | 
			
		||||
		Assert.notNull(properties, "Properties must not be null");
 | 
			
		||||
		this.recoveryManager = recoveryManager;
 | 
			
		||||
		this.properties = properties;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public DataSource wrapDataSource(XADataSource dataSource) {
 | 
			
		||||
		XAResourceRecoveryHelper recoveryHelper = getRecoveryHelper(dataSource);
 | 
			
		||||
		this.recoveryManager.registerXAResourceRecoveryHelper(recoveryHelper);
 | 
			
		||||
		return new NarayanaDataSourceBean(dataSource);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private XAResourceRecoveryHelper getRecoveryHelper(XADataSource dataSource) {
 | 
			
		||||
		if (this.properties.getRecoveryDbUser() == null
 | 
			
		||||
				&& this.properties.getRecoveryDbPass() == null) {
 | 
			
		||||
			return new DataSourceXAResourceRecoveryHelper(dataSource);
 | 
			
		||||
		}
 | 
			
		||||
		return new DataSourceXAResourceRecoveryHelper(dataSource,
 | 
			
		||||
				this.properties.getRecoveryDbUser(), this.properties.getRecoveryDbPass());
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,20 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2015 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Support classes for Narayana JTA.
 | 
			
		||||
 */
 | 
			
		||||
package org.springframework.boot.jta.narayana;
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,174 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2016 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.springframework.boot.jta.narayana;
 | 
			
		||||
 | 
			
		||||
import java.sql.SQLException;
 | 
			
		||||
 | 
			
		||||
import javax.sql.XAConnection;
 | 
			
		||||
import javax.sql.XADataSource;
 | 
			
		||||
import javax.transaction.xa.XAException;
 | 
			
		||||
import javax.transaction.xa.XAResource;
 | 
			
		||||
 | 
			
		||||
import org.junit.Before;
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
import static org.mockito.BDDMockito.given;
 | 
			
		||||
import static org.mockito.Matchers.anyString;
 | 
			
		||||
import static org.mockito.Mockito.mock;
 | 
			
		||||
import static org.mockito.Mockito.times;
 | 
			
		||||
import static org.mockito.Mockito.verify;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tests for {@link DataSourceXAResourceRecoveryHelper}.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Gytis Trikleris
 | 
			
		||||
 */
 | 
			
		||||
public class DataSourceXAResourceRecoveryHelperTests {
 | 
			
		||||
 | 
			
		||||
	private XADataSource xaDataSource;
 | 
			
		||||
 | 
			
		||||
	private XAConnection xaConnection;
 | 
			
		||||
 | 
			
		||||
	private XAResource xaResource;
 | 
			
		||||
 | 
			
		||||
	private DataSourceXAResourceRecoveryHelper recoveryHelper;
 | 
			
		||||
 | 
			
		||||
	@Before
 | 
			
		||||
	public void before() throws SQLException {
 | 
			
		||||
		this.xaDataSource = mock(XADataSource.class);
 | 
			
		||||
		this.xaConnection = mock(XAConnection.class);
 | 
			
		||||
		this.xaResource = mock(XAResource.class);
 | 
			
		||||
		this.recoveryHelper = new DataSourceXAResourceRecoveryHelper(this.xaDataSource);
 | 
			
		||||
 | 
			
		||||
		given(this.xaDataSource.getXAConnection()).willReturn(this.xaConnection);
 | 
			
		||||
		given(this.xaConnection.getXAResource()).willReturn(this.xaResource);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void shouldCreateConnectionAndGetXAResource() throws SQLException {
 | 
			
		||||
		XAResource[] xaResources = this.recoveryHelper.getXAResources();
 | 
			
		||||
		assertThat(xaResources.length).isEqualTo(1);
 | 
			
		||||
		assertThat(xaResources[0]).isSameAs(this.recoveryHelper);
 | 
			
		||||
		verify(this.xaDataSource, times(1)).getXAConnection();
 | 
			
		||||
		verify(this.xaConnection, times(1)).getXAResource();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void shouldCreateConnectionWithCredentialsAndGetXAResource()
 | 
			
		||||
			throws SQLException {
 | 
			
		||||
		given(this.xaDataSource.getXAConnection(anyString(), anyString()))
 | 
			
		||||
				.willReturn(this.xaConnection);
 | 
			
		||||
		this.recoveryHelper = new DataSourceXAResourceRecoveryHelper(this.xaDataSource,
 | 
			
		||||
				"username", "password");
 | 
			
		||||
		XAResource[] xaResources = this.recoveryHelper.getXAResources();
 | 
			
		||||
		assertThat(xaResources.length).isEqualTo(1);
 | 
			
		||||
		assertThat(xaResources[0]).isSameAs(this.recoveryHelper);
 | 
			
		||||
		verify(this.xaDataSource, times(1)).getXAConnection("username", "password");
 | 
			
		||||
		verify(this.xaConnection, times(1)).getXAResource();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void shouldFailToCreateConnectionAndNotGetXAResource() throws SQLException {
 | 
			
		||||
		given(this.xaDataSource.getXAConnection())
 | 
			
		||||
				.willThrow(new SQLException("Test exception"));
 | 
			
		||||
		XAResource[] xaResources = this.recoveryHelper.getXAResources();
 | 
			
		||||
		assertThat(xaResources.length).isEqualTo(0);
 | 
			
		||||
		verify(this.xaDataSource, times(1)).getXAConnection();
 | 
			
		||||
		verify(this.xaConnection, times(0)).getXAResource();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void shouldDelegateRecoverCall() throws XAException {
 | 
			
		||||
		this.recoveryHelper.getXAResources();
 | 
			
		||||
		this.recoveryHelper.recover(XAResource.TMSTARTRSCAN);
 | 
			
		||||
		verify(this.xaResource, times(1)).recover(XAResource.TMSTARTRSCAN);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void shouldDelegateRecoverCallAndCloseConnection()
 | 
			
		||||
			throws XAException, SQLException {
 | 
			
		||||
		this.recoveryHelper.getXAResources();
 | 
			
		||||
		this.recoveryHelper.recover(XAResource.TMENDRSCAN);
 | 
			
		||||
		verify(this.xaResource, times(1)).recover(XAResource.TMENDRSCAN);
 | 
			
		||||
		verify(this.xaConnection, times(1)).close();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void shouldDelegateStartCall() throws XAException {
 | 
			
		||||
		this.recoveryHelper.getXAResources();
 | 
			
		||||
		this.recoveryHelper.start(null, 0);
 | 
			
		||||
		verify(this.xaResource, times(1)).start(null, 0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void shouldDelegateEndCall() throws XAException {
 | 
			
		||||
		this.recoveryHelper.getXAResources();
 | 
			
		||||
		this.recoveryHelper.end(null, 0);
 | 
			
		||||
		verify(this.xaResource, times(1)).end(null, 0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void shouldDelegatePrepareCall() throws XAException {
 | 
			
		||||
		this.recoveryHelper.getXAResources();
 | 
			
		||||
		this.recoveryHelper.prepare(null);
 | 
			
		||||
		verify(this.xaResource, times(1)).prepare(null);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void shouldDelegateCommitCall() throws XAException {
 | 
			
		||||
		this.recoveryHelper.getXAResources();
 | 
			
		||||
		this.recoveryHelper.commit(null, true);
 | 
			
		||||
		verify(this.xaResource, times(1)).commit(null, true);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void shouldDelegateRollbackCall() throws XAException {
 | 
			
		||||
		this.recoveryHelper.getXAResources();
 | 
			
		||||
		this.recoveryHelper.rollback(null);
 | 
			
		||||
		verify(this.xaResource, times(1)).rollback(null);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void shouldDelegateIsSameRMCall() throws XAException {
 | 
			
		||||
		this.recoveryHelper.getXAResources();
 | 
			
		||||
		this.recoveryHelper.isSameRM(null);
 | 
			
		||||
		verify(this.xaResource, times(1)).isSameRM(null);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void shouldDelegateForgetCall() throws XAException {
 | 
			
		||||
		this.recoveryHelper.getXAResources();
 | 
			
		||||
		this.recoveryHelper.forget(null);
 | 
			
		||||
		verify(this.xaResource, times(1)).forget(null);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void shouldDelegateGetTransactionTimeoutCall() throws XAException {
 | 
			
		||||
		this.recoveryHelper.getXAResources();
 | 
			
		||||
		this.recoveryHelper.getTransactionTimeout();
 | 
			
		||||
		verify(this.xaResource, times(1)).getTransactionTimeout();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void shouldDelegateSetTransactionTimeoutCall() throws XAException {
 | 
			
		||||
		this.recoveryHelper.getXAResources();
 | 
			
		||||
		this.recoveryHelper.setTransactionTimeout(0);
 | 
			
		||||
		verify(this.xaResource, times(1)).setTransactionTimeout(0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,90 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2016 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.springframework.boot.jta.narayana;
 | 
			
		||||
 | 
			
		||||
import javax.jms.ConnectionFactory;
 | 
			
		||||
import javax.sql.DataSource;
 | 
			
		||||
import javax.transaction.TransactionManager;
 | 
			
		||||
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
 | 
			
		||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
 | 
			
		||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
 | 
			
		||||
import org.springframework.context.annotation.Bean;
 | 
			
		||||
import org.springframework.context.annotation.Configuration;
 | 
			
		||||
 | 
			
		||||
import static org.mockito.Mockito.mock;
 | 
			
		||||
import static org.mockito.Mockito.spy;
 | 
			
		||||
import static org.mockito.Mockito.verify;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tests for {@link NarayanaBeanFactoryPostProcessor}.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Gytis Trikleris
 | 
			
		||||
 */
 | 
			
		||||
public class NarayanaBeanFactoryPostProcessorTests {
 | 
			
		||||
 | 
			
		||||
	private AnnotationConfigApplicationContext context;
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void setsDependsOn() {
 | 
			
		||||
		DefaultListableBeanFactory beanFactory = spy(new DefaultListableBeanFactory());
 | 
			
		||||
		this.context = new AnnotationConfigApplicationContext(beanFactory);
 | 
			
		||||
		this.context.register(Config.class);
 | 
			
		||||
		this.context.refresh();
 | 
			
		||||
		verify(beanFactory).registerDependentBean("narayanaTransactionManager",
 | 
			
		||||
				"dataSource");
 | 
			
		||||
		verify(beanFactory).registerDependentBean("narayanaTransactionManager",
 | 
			
		||||
				"connectionFactory");
 | 
			
		||||
		verify(beanFactory).registerDependentBean("narayanaRecoveryManagerBean",
 | 
			
		||||
				"dataSource");
 | 
			
		||||
		verify(beanFactory).registerDependentBean("narayanaRecoveryManagerBean",
 | 
			
		||||
				"connectionFactory");
 | 
			
		||||
		this.context.close();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Configuration
 | 
			
		||||
	static class Config {
 | 
			
		||||
 | 
			
		||||
		@Bean
 | 
			
		||||
		public DataSource dataSource() {
 | 
			
		||||
			return mock(DataSource.class);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Bean
 | 
			
		||||
		public ConnectionFactory connectionFactory() {
 | 
			
		||||
			return mock(ConnectionFactory.class);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Bean
 | 
			
		||||
		public TransactionManager narayanaTransactionManager() {
 | 
			
		||||
			return mock(TransactionManager.class);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Bean
 | 
			
		||||
		public NarayanaRecoveryManagerBean narayanaRecoveryManagerBean() {
 | 
			
		||||
			return mock(NarayanaRecoveryManagerBean.class);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Bean
 | 
			
		||||
		public static NarayanaBeanFactoryPostProcessor narayanaPostProcessor() {
 | 
			
		||||
			return new NarayanaBeanFactoryPostProcessor();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,136 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2016 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.springframework.boot.jta.narayana;
 | 
			
		||||
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import com.arjuna.ats.arjuna.common.CoordinatorEnvironmentBean;
 | 
			
		||||
import com.arjuna.ats.arjuna.common.CoreEnvironmentBean;
 | 
			
		||||
import com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean;
 | 
			
		||||
import com.arjuna.ats.arjuna.common.RecoveryEnvironmentBean;
 | 
			
		||||
import com.arjuna.ats.jta.common.JTAEnvironmentBean;
 | 
			
		||||
import com.arjuna.common.internal.util.propertyservice.BeanPopulator;
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tests for {@link NarayanaConfigurationBean}.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Gytis Trikleris
 | 
			
		||||
 */
 | 
			
		||||
public class NarayanaConfigurationBeanTests {
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void shouldSetDefaultProperties() throws Exception {
 | 
			
		||||
		NarayanaProperties narayanaProperties = new NarayanaProperties();
 | 
			
		||||
		NarayanaConfigurationBean narayanaConfigurationBean = new NarayanaConfigurationBean(
 | 
			
		||||
				narayanaProperties);
 | 
			
		||||
		narayanaConfigurationBean.afterPropertiesSet();
 | 
			
		||||
 | 
			
		||||
		assertThat(BeanPopulator.getDefaultInstance(CoreEnvironmentBean.class)
 | 
			
		||||
				.getNodeIdentifier()).isEqualTo("1");
 | 
			
		||||
		assertThat(BeanPopulator.getDefaultInstance(ObjectStoreEnvironmentBean.class)
 | 
			
		||||
				.getObjectStoreDir()).isEqualTo("target/tx-object-store");
 | 
			
		||||
		assertThat(BeanPopulator
 | 
			
		||||
				.getNamedInstance(ObjectStoreEnvironmentBean.class, "communicationStore")
 | 
			
		||||
				.getObjectStoreDir()).isEqualTo("target/tx-object-store");
 | 
			
		||||
		assertThat(BeanPopulator
 | 
			
		||||
				.getNamedInstance(ObjectStoreEnvironmentBean.class, "stateStore")
 | 
			
		||||
				.getObjectStoreDir()).isEqualTo("target/tx-object-store");
 | 
			
		||||
		assertThat(BeanPopulator.getDefaultInstance(CoordinatorEnvironmentBean.class)
 | 
			
		||||
				.isCommitOnePhase()).isTrue();
 | 
			
		||||
		assertThat(BeanPopulator.getDefaultInstance(CoordinatorEnvironmentBean.class)
 | 
			
		||||
				.getDefaultTimeout()).isEqualTo(60);
 | 
			
		||||
		assertThat(BeanPopulator.getDefaultInstance(RecoveryEnvironmentBean.class)
 | 
			
		||||
				.getPeriodicRecoveryPeriod()).isEqualTo(120);
 | 
			
		||||
		assertThat(BeanPopulator.getDefaultInstance(RecoveryEnvironmentBean.class)
 | 
			
		||||
				.getRecoveryBackoffPeriod()).isEqualTo(10);
 | 
			
		||||
 | 
			
		||||
		List<String> xaResourceOrphanFilters = Arrays.asList(
 | 
			
		||||
				"com.arjuna.ats.internal.jta.recovery.arjunacore.JTATransactionLogXAResourceOrphanFilter",
 | 
			
		||||
				"com.arjuna.ats.internal.jta.recovery.arjunacore.JTANodeNameXAResourceOrphanFilter");
 | 
			
		||||
		assertThat(BeanPopulator.getDefaultInstance(JTAEnvironmentBean.class)
 | 
			
		||||
				.getXaResourceOrphanFilterClassNames())
 | 
			
		||||
						.isEqualTo(xaResourceOrphanFilters);
 | 
			
		||||
 | 
			
		||||
		List<String> recoveryModules = Arrays.asList(
 | 
			
		||||
				"com.arjuna.ats.internal.arjuna.recovery.AtomicActionRecoveryModule",
 | 
			
		||||
				"com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule");
 | 
			
		||||
		assertThat(BeanPopulator.getDefaultInstance(RecoveryEnvironmentBean.class)
 | 
			
		||||
				.getRecoveryModuleClassNames()).isEqualTo(recoveryModules);
 | 
			
		||||
 | 
			
		||||
		List<String> expiryScanners = Arrays.asList(
 | 
			
		||||
				"com.arjuna.ats.internal.arjuna.recovery.ExpiredTransactionStatusManagerScanner");
 | 
			
		||||
		assertThat(BeanPopulator.getDefaultInstance(RecoveryEnvironmentBean.class)
 | 
			
		||||
				.getExpiryScannerClassNames()).isEqualTo(expiryScanners);
 | 
			
		||||
 | 
			
		||||
		assertThat(BeanPopulator.getDefaultInstance(JTAEnvironmentBean.class)
 | 
			
		||||
				.getXaResourceRecoveryClassNames()).isEmpty();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void shouldSetModifiedProperties() throws Exception {
 | 
			
		||||
		NarayanaProperties narayanaProperties = new NarayanaProperties();
 | 
			
		||||
		narayanaProperties.setTransactionManagerId("test-id");
 | 
			
		||||
		narayanaProperties.setLogDir("test-dir");
 | 
			
		||||
		narayanaProperties.setDefaultTimeout(1);
 | 
			
		||||
		narayanaProperties.setPeriodicRecoveryPeriod(2);
 | 
			
		||||
		narayanaProperties.setRecoveryBackoffPeriod(3);
 | 
			
		||||
		narayanaProperties.setOnePhaseCommit(false);
 | 
			
		||||
		narayanaProperties.setXaResourceOrphanFilters(
 | 
			
		||||
				Arrays.asList("test-filter-1", "test-filter-2"));
 | 
			
		||||
		narayanaProperties
 | 
			
		||||
				.setRecoveryModules(Arrays.asList("test-module-1", "test-module-2"));
 | 
			
		||||
		narayanaProperties
 | 
			
		||||
				.setExpiryScanners(Arrays.asList("test-scanner-1", "test-scanner-2"));
 | 
			
		||||
 | 
			
		||||
		NarayanaConfigurationBean narayanaConfigurationBean = new NarayanaConfigurationBean(
 | 
			
		||||
				narayanaProperties);
 | 
			
		||||
		narayanaConfigurationBean.afterPropertiesSet();
 | 
			
		||||
 | 
			
		||||
		assertThat(BeanPopulator.getDefaultInstance(CoreEnvironmentBean.class)
 | 
			
		||||
				.getNodeIdentifier()).isEqualTo("test-id");
 | 
			
		||||
		assertThat(BeanPopulator.getDefaultInstance(ObjectStoreEnvironmentBean.class)
 | 
			
		||||
				.getObjectStoreDir()).isEqualTo("test-dir");
 | 
			
		||||
		assertThat(BeanPopulator
 | 
			
		||||
				.getNamedInstance(ObjectStoreEnvironmentBean.class, "communicationStore")
 | 
			
		||||
				.getObjectStoreDir()).isEqualTo("test-dir");
 | 
			
		||||
		assertThat(BeanPopulator
 | 
			
		||||
				.getNamedInstance(ObjectStoreEnvironmentBean.class, "stateStore")
 | 
			
		||||
				.getObjectStoreDir()).isEqualTo("test-dir");
 | 
			
		||||
		assertThat(BeanPopulator.getDefaultInstance(CoordinatorEnvironmentBean.class)
 | 
			
		||||
				.isCommitOnePhase()).isFalse();
 | 
			
		||||
		assertThat(BeanPopulator.getDefaultInstance(CoordinatorEnvironmentBean.class)
 | 
			
		||||
				.getDefaultTimeout()).isEqualTo(1);
 | 
			
		||||
		assertThat(BeanPopulator.getDefaultInstance(RecoveryEnvironmentBean.class)
 | 
			
		||||
				.getPeriodicRecoveryPeriod()).isEqualTo(2);
 | 
			
		||||
		assertThat(BeanPopulator.getDefaultInstance(RecoveryEnvironmentBean.class)
 | 
			
		||||
				.getRecoveryBackoffPeriod()).isEqualTo(3);
 | 
			
		||||
		assertThat(BeanPopulator.getDefaultInstance(JTAEnvironmentBean.class)
 | 
			
		||||
				.getXaResourceOrphanFilterClassNames())
 | 
			
		||||
						.isEqualTo(Arrays.asList("test-filter-1", "test-filter-2"));
 | 
			
		||||
		assertThat(BeanPopulator.getDefaultInstance(RecoveryEnvironmentBean.class)
 | 
			
		||||
				.getRecoveryModuleClassNames())
 | 
			
		||||
						.isEqualTo(Arrays.asList("test-module-1", "test-module-2"));
 | 
			
		||||
		assertThat(BeanPopulator.getDefaultInstance(RecoveryEnvironmentBean.class)
 | 
			
		||||
				.getExpiryScannerClassNames())
 | 
			
		||||
						.isEqualTo(Arrays.asList("test-scanner-1", "test-scanner-2"));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,126 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2016 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.springframework.boot.jta.narayana;
 | 
			
		||||
 | 
			
		||||
import java.sql.Connection;
 | 
			
		||||
import java.sql.SQLException;
 | 
			
		||||
import java.util.Properties;
 | 
			
		||||
 | 
			
		||||
import javax.sql.DataSource;
 | 
			
		||||
import javax.sql.XAConnection;
 | 
			
		||||
import javax.sql.XADataSource;
 | 
			
		||||
 | 
			
		||||
import com.arjuna.ats.internal.jdbc.ConnectionImple;
 | 
			
		||||
import com.arjuna.ats.jdbc.TransactionalDriver;
 | 
			
		||||
import org.junit.Before;
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
import static org.mockito.BDDMockito.given;
 | 
			
		||||
import static org.mockito.Mockito.mock;
 | 
			
		||||
import static org.mockito.Mockito.times;
 | 
			
		||||
import static org.mockito.Mockito.verify;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tests for {@link NarayanaDataSourceBean}.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Gytis Trikleris
 | 
			
		||||
 */
 | 
			
		||||
public class NarayanaDataSourceBeanTests {
 | 
			
		||||
 | 
			
		||||
	private XADataSource dataSource;
 | 
			
		||||
 | 
			
		||||
	private NarayanaDataSourceBean dataSourceBean;
 | 
			
		||||
 | 
			
		||||
	@Before
 | 
			
		||||
	public void before() {
 | 
			
		||||
		this.dataSource = mock(XADataSource.class);
 | 
			
		||||
		this.dataSourceBean = new NarayanaDataSourceBean(this.dataSource);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void shouldBeAWrapper() throws SQLException {
 | 
			
		||||
		assertThat(this.dataSourceBean.isWrapperFor(DataSource.class)).isTrue();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void shouldNotBeAWrapper() throws SQLException {
 | 
			
		||||
		assertThat(this.dataSourceBean.isWrapperFor(XADataSource.class)).isFalse();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void shouldUnwrapDataSource() throws SQLException {
 | 
			
		||||
		assertThat(this.dataSourceBean.unwrap(DataSource.class))
 | 
			
		||||
				.isInstanceOf(DataSource.class);
 | 
			
		||||
		assertThat(this.dataSourceBean.unwrap(DataSource.class))
 | 
			
		||||
				.isSameAs(this.dataSourceBean);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void shouldUnwrapXaDataSource() throws SQLException {
 | 
			
		||||
		assertThat(this.dataSourceBean.unwrap(XADataSource.class))
 | 
			
		||||
				.isInstanceOf(XADataSource.class);
 | 
			
		||||
		assertThat(this.dataSourceBean.unwrap(XADataSource.class))
 | 
			
		||||
				.isSameAs(this.dataSource);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void shouldGetConnectionAndCommit() throws SQLException {
 | 
			
		||||
		Connection mockConnection = mock(Connection.class);
 | 
			
		||||
		XAConnection mockXaConnection = mock(XAConnection.class);
 | 
			
		||||
		given(mockXaConnection.getConnection()).willReturn(mockConnection);
 | 
			
		||||
		given(this.dataSource.getXAConnection()).willReturn(mockXaConnection);
 | 
			
		||||
 | 
			
		||||
		Properties properties = new Properties();
 | 
			
		||||
		properties.put(TransactionalDriver.XADataSource, this.dataSource);
 | 
			
		||||
 | 
			
		||||
		Connection connection = this.dataSourceBean.getConnection();
 | 
			
		||||
		assertThat(connection).isInstanceOf(ConnectionImple.class);
 | 
			
		||||
 | 
			
		||||
		connection.commit();
 | 
			
		||||
 | 
			
		||||
		verify(this.dataSource, times(1)).getXAConnection();
 | 
			
		||||
		verify(mockXaConnection, times(1)).getConnection();
 | 
			
		||||
		verify(mockConnection, times(1)).commit();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void shouldGetConnectionAndCommitWithCredentials() throws SQLException {
 | 
			
		||||
		String username = "testUsername";
 | 
			
		||||
		String password = "testPassword";
 | 
			
		||||
		Connection mockConnection = mock(Connection.class);
 | 
			
		||||
		XAConnection mockXaConnection = mock(XAConnection.class);
 | 
			
		||||
		given(mockXaConnection.getConnection()).willReturn(mockConnection);
 | 
			
		||||
		given(this.dataSource.getXAConnection(username, password))
 | 
			
		||||
				.willReturn(mockXaConnection);
 | 
			
		||||
 | 
			
		||||
		Properties properties = new Properties();
 | 
			
		||||
		properties.put(TransactionalDriver.XADataSource, this.dataSource);
 | 
			
		||||
		properties.put(TransactionalDriver.userName, username);
 | 
			
		||||
		properties.put(TransactionalDriver.password, password);
 | 
			
		||||
 | 
			
		||||
		Connection connection = this.dataSourceBean.getConnection(username, password);
 | 
			
		||||
		assertThat(connection).isInstanceOf(ConnectionImple.class);
 | 
			
		||||
 | 
			
		||||
		connection.commit();
 | 
			
		||||
 | 
			
		||||
		verify(this.dataSource, times(1)).getXAConnection(username, password);
 | 
			
		||||
		verify(mockXaConnection, times(1)).getConnection();
 | 
			
		||||
		verify(mockConnection, times(1)).commit();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,58 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2016 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.springframework.boot.jta.narayana;
 | 
			
		||||
 | 
			
		||||
import com.arjuna.ats.jbossatx.jta.RecoveryManagerService;
 | 
			
		||||
import org.junit.Before;
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
 | 
			
		||||
import static org.mockito.Mockito.mock;
 | 
			
		||||
import static org.mockito.Mockito.times;
 | 
			
		||||
import static org.mockito.Mockito.verify;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tests for {@link NarayanaRecoveryManagerBean}.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Gytis Trikleris
 | 
			
		||||
 */
 | 
			
		||||
public class NarayanaRecoveryManagerBeanTests {
 | 
			
		||||
 | 
			
		||||
	private RecoveryManagerService service;
 | 
			
		||||
 | 
			
		||||
	private NarayanaRecoveryManagerBean recoveryManager;
 | 
			
		||||
 | 
			
		||||
	@Before
 | 
			
		||||
	public void before() {
 | 
			
		||||
		this.service = mock(RecoveryManagerService.class);
 | 
			
		||||
		this.recoveryManager = new NarayanaRecoveryManagerBean(this.service);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void shouldCreateAndStartRecoveryManagerService() throws Exception {
 | 
			
		||||
		this.recoveryManager.afterPropertiesSet();
 | 
			
		||||
		verify(this.service, times(1)).create();
 | 
			
		||||
		verify(this.service, times(1)).start();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void shouldStopAndDestroyRecoveryManagerService() throws Exception {
 | 
			
		||||
		this.recoveryManager.destroy();
 | 
			
		||||
		verify(this.service, times(1)).stop();
 | 
			
		||||
		verify(this.service, times(1)).destroy();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,77 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2016 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.springframework.boot.jta.narayana;
 | 
			
		||||
 | 
			
		||||
import javax.jms.ConnectionFactory;
 | 
			
		||||
import javax.jms.XAConnectionFactory;
 | 
			
		||||
import javax.transaction.TransactionManager;
 | 
			
		||||
 | 
			
		||||
import org.jboss.narayana.jta.jms.ConnectionFactoryProxy;
 | 
			
		||||
import org.jboss.narayana.jta.jms.JmsXAResourceRecoveryHelper;
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
import static org.mockito.BDDMockito.given;
 | 
			
		||||
import static org.mockito.Matchers.any;
 | 
			
		||||
import static org.mockito.Mockito.mock;
 | 
			
		||||
import static org.mockito.Mockito.times;
 | 
			
		||||
import static org.mockito.Mockito.verify;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tests for {@link NarayanaXAConnectionFactoryWrapper}.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Gytis Trikleris
 | 
			
		||||
 */
 | 
			
		||||
public class NarayanaXAConnectionFactoryWrapperTests {
 | 
			
		||||
 | 
			
		||||
	private XAConnectionFactory connectionFactory = mock(XAConnectionFactory.class);
 | 
			
		||||
 | 
			
		||||
	private TransactionManager transactionManager = mock(TransactionManager.class);
 | 
			
		||||
 | 
			
		||||
	private NarayanaRecoveryManagerBean recoveryManager = mock(
 | 
			
		||||
			NarayanaRecoveryManagerBean.class);
 | 
			
		||||
 | 
			
		||||
	private NarayanaProperties properties = mock(NarayanaProperties.class);
 | 
			
		||||
 | 
			
		||||
	private NarayanaXAConnectionFactoryWrapper wrapper = new NarayanaXAConnectionFactoryWrapper(
 | 
			
		||||
			this.transactionManager, this.recoveryManager, this.properties);
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void wrap() {
 | 
			
		||||
		ConnectionFactory wrapped = this.wrapper
 | 
			
		||||
				.wrapConnectionFactory(this.connectionFactory);
 | 
			
		||||
		assertThat(wrapped).isInstanceOf(ConnectionFactoryProxy.class);
 | 
			
		||||
		verify(this.recoveryManager, times(1))
 | 
			
		||||
				.registerXAResourceRecoveryHelper(any(JmsXAResourceRecoveryHelper.class));
 | 
			
		||||
		verify(this.properties, times(1)).getRecoveryJmsUser();
 | 
			
		||||
		verify(this.properties, times(1)).getRecoveryJmsPass();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void wrapWithCredentials() {
 | 
			
		||||
		given(this.properties.getRecoveryJmsUser()).willReturn("userName");
 | 
			
		||||
		given(this.properties.getRecoveryJmsPass()).willReturn("password");
 | 
			
		||||
		ConnectionFactory wrapped = this.wrapper
 | 
			
		||||
				.wrapConnectionFactory(this.connectionFactory);
 | 
			
		||||
		assertThat(wrapped).isInstanceOf(ConnectionFactoryProxy.class);
 | 
			
		||||
		verify(this.recoveryManager, times(1))
 | 
			
		||||
				.registerXAResourceRecoveryHelper(any(JmsXAResourceRecoveryHelper.class));
 | 
			
		||||
		verify(this.properties, times(2)).getRecoveryJmsUser();
 | 
			
		||||
		verify(this.properties, times(1)).getRecoveryJmsPass();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,70 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2016 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package org.springframework.boot.jta.narayana;
 | 
			
		||||
 | 
			
		||||
import javax.sql.DataSource;
 | 
			
		||||
import javax.sql.XADataSource;
 | 
			
		||||
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
import static org.mockito.BDDMockito.given;
 | 
			
		||||
import static org.mockito.Matchers.any;
 | 
			
		||||
import static org.mockito.Mockito.mock;
 | 
			
		||||
import static org.mockito.Mockito.times;
 | 
			
		||||
import static org.mockito.Mockito.verify;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tests for {@link NarayanaXADataSourceWrapper}.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Gytis Trikleris
 | 
			
		||||
 */
 | 
			
		||||
public class NarayanaXADataSourceWrapperTests {
 | 
			
		||||
 | 
			
		||||
	private XADataSource dataSource = mock(XADataSource.class);
 | 
			
		||||
 | 
			
		||||
	private NarayanaRecoveryManagerBean recoveryManager = mock(
 | 
			
		||||
			NarayanaRecoveryManagerBean.class);
 | 
			
		||||
 | 
			
		||||
	private NarayanaProperties properties = mock(NarayanaProperties.class);
 | 
			
		||||
 | 
			
		||||
	private NarayanaXADataSourceWrapper wrapper = new NarayanaXADataSourceWrapper(
 | 
			
		||||
			this.recoveryManager, this.properties);
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void wrap() {
 | 
			
		||||
		DataSource wrapped = this.wrapper.wrapDataSource(this.dataSource);
 | 
			
		||||
		assertThat(wrapped).isInstanceOf(NarayanaDataSourceBean.class);
 | 
			
		||||
		verify(this.recoveryManager, times(1)).registerXAResourceRecoveryHelper(
 | 
			
		||||
				any(DataSourceXAResourceRecoveryHelper.class));
 | 
			
		||||
		verify(this.properties, times(1)).getRecoveryDbUser();
 | 
			
		||||
		verify(this.properties, times(1)).getRecoveryDbPass();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void wrapWithCredentials() {
 | 
			
		||||
		given(this.properties.getRecoveryDbUser()).willReturn("userName");
 | 
			
		||||
		given(this.properties.getRecoveryDbPass()).willReturn("password");
 | 
			
		||||
		DataSource wrapped = this.wrapper.wrapDataSource(this.dataSource);
 | 
			
		||||
		assertThat(wrapped).isInstanceOf(NarayanaDataSourceBean.class);
 | 
			
		||||
		verify(this.recoveryManager, times(1)).registerXAResourceRecoveryHelper(
 | 
			
		||||
				any(DataSourceXAResourceRecoveryHelper.class));
 | 
			
		||||
		verify(this.properties, times(2)).getRecoveryDbUser();
 | 
			
		||||
		verify(this.properties, times(1)).getRecoveryDbPass();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue