Browse Source
* spring-boot-starter-jta-narayana: Polish contribution Add Narayana JTA supportpull/5622/head
35 changed files with 2332 additions and 1 deletions
@ -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); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -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; |
||||||
|
} |
||||||
|
|
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -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 |
||||||
@ -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