Repository: liuyangming/ByteJTA
Branch: master
Commit: fdaf029d80d7
Files: 175
Total size: 852.1 KB
Directory structure:
gitextract_1m8l_8vc/
├── .github/
│ └── FUNDING.yml
├── .gitignore
├── LICENSE
├── README-zh.md
├── README.md
├── bytejta-core/
│ ├── pom.xml
│ └── src/
│ └── main/
│ └── java/
│ └── org/
│ └── bytesoft/
│ ├── bytejta/
│ │ ├── TransactionBeanFactoryImpl.java
│ │ ├── TransactionCoordinator.java
│ │ ├── TransactionImpl.java
│ │ ├── TransactionManagerImpl.java
│ │ ├── TransactionRecoveryImpl.java
│ │ ├── TransactionRepositoryImpl.java
│ │ ├── TransactionStrategy.java
│ │ ├── UserTransactionImpl.java
│ │ ├── VacantTransactionLock.java
│ │ ├── logging/
│ │ │ ├── ArchiveDeserializerImpl.java
│ │ │ ├── SampleTransactionLogger.java
│ │ │ ├── deserializer/
│ │ │ │ ├── TransactionArchiveDeserializer.java
│ │ │ │ └── XAResourceArchiveDeserializer.java
│ │ │ └── store/
│ │ │ ├── VirtualLoggingFile.java
│ │ │ └── VirtualLoggingSystemImpl.java
│ │ ├── resource/
│ │ │ ├── XATerminatorImpl.java
│ │ │ └── XATerminatorOptd.java
│ │ ├── strategy/
│ │ │ ├── CommonTransactionStrategy.java
│ │ │ ├── LastResourceOptimizeStrategy.java
│ │ │ ├── SimpleTransactionStrategy.java
│ │ │ └── VacantTransactionStrategy.java
│ │ ├── supports/
│ │ │ ├── jdbc/
│ │ │ │ ├── DataSourceHolder.java
│ │ │ │ ├── LocalXACompatible.java
│ │ │ │ ├── LocalXAConnection.java
│ │ │ │ ├── LocalXAResource.java
│ │ │ │ ├── LogicalConnection.java
│ │ │ │ └── RecoveredResource.java
│ │ │ └── resource/
│ │ │ ├── CommonResourceDescriptor.java
│ │ │ ├── LocalXAResourceDescriptor.java
│ │ │ ├── RemoteResourceDescriptor.java
│ │ │ └── UnidentifiedResourceDescriptor.java
│ │ ├── work/
│ │ │ └── TransactionWork.java
│ │ └── xa/
│ │ └── XidFactoryImpl.java
│ ├── common/
│ │ └── utils/
│ │ ├── ByteUtils.java
│ │ ├── CommonUtils.java
│ │ └── SerializeUtils.java
│ └── transaction/
│ ├── CommitRequiredException.java
│ ├── RemoteSystemException.java
│ ├── RollbackRequiredException.java
│ ├── Transaction.java
│ ├── TransactionBeanFactory.java
│ ├── TransactionContext.java
│ ├── TransactionException.java
│ ├── TransactionLock.java
│ ├── TransactionManager.java
│ ├── TransactionParticipant.java
│ ├── TransactionRecovery.java
│ ├── TransactionRepository.java
│ ├── adapter/
│ │ └── ResourceAdapterImpl.java
│ ├── archive/
│ │ ├── TransactionArchive.java
│ │ └── XAResourceArchive.java
│ ├── aware/
│ │ ├── TransactionBeanFactoryAware.java
│ │ ├── TransactionDebuggable.java
│ │ └── TransactionEndpointAware.java
│ ├── cmd/
│ │ └── CommandDispatcher.java
│ ├── internal/
│ │ ├── SynchronizationImpl.java
│ │ ├── SynchronizationList.java
│ │ ├── TransactionListenerList.java
│ │ └── TransactionResourceListenerList.java
│ ├── logging/
│ │ ├── ArchiveDeserializer.java
│ │ ├── LoggingFlushable.java
│ │ ├── TransactionLogger.java
│ │ └── store/
│ │ ├── VirtualLoggingKey.java
│ │ ├── VirtualLoggingListener.java
│ │ ├── VirtualLoggingRecord.java
│ │ ├── VirtualLoggingSystem.java
│ │ └── VirtualLoggingTrigger.java
│ ├── recovery/
│ │ ├── TransactionRecoveryCallback.java
│ │ └── TransactionRecoveryListener.java
│ ├── remote/
│ │ ├── RemoteAddr.java
│ │ ├── RemoteCoordinator.java
│ │ ├── RemoteNode.java
│ │ └── RemoteSvc.java
│ ├── resource/
│ │ └── XATerminator.java
│ ├── supports/
│ │ ├── TransactionExtra.java
│ │ ├── TransactionListener.java
│ │ ├── TransactionListenerAdapter.java
│ │ ├── TransactionResourceListener.java
│ │ ├── TransactionResourceListenerAdapter.java
│ │ ├── TransactionStatistic.java
│ │ ├── TransactionTimer.java
│ │ ├── resource/
│ │ │ └── XAResourceDescriptor.java
│ │ ├── rpc/
│ │ │ ├── TransactionInterceptor.java
│ │ │ ├── TransactionRequest.java
│ │ │ └── TransactionResponse.java
│ │ └── serialize/
│ │ └── XAResourceDeserializer.java
│ ├── work/
│ │ ├── SimpleWork.java
│ │ ├── SimpleWorkListener.java
│ │ └── SimpleWorkManager.java
│ └── xa/
│ ├── TransactionXid.java
│ └── XidFactory.java
├── bytejta-supports/
│ ├── pom.xml
│ └── src/
│ └── main/
│ ├── java/
│ │ └── org/
│ │ └── bytesoft/
│ │ └── bytejta/
│ │ └── supports/
│ │ ├── boot/
│ │ │ └── jdbc/
│ │ │ ├── DataSourceCciBuilder.java
│ │ │ └── DataSourceSpiBuilder.java
│ │ ├── internal/
│ │ │ ├── RemoteCoordinatorRegistry.java
│ │ │ └── TransactionCommandDispatcher.java
│ │ ├── jdbc/
│ │ │ └── LocalXADataSource.java
│ │ ├── jpa/
│ │ │ └── hibernate/
│ │ │ └── HibernateJtaPlatform.java
│ │ ├── resource/
│ │ │ ├── ManagedConnectionFactoryHandler.java
│ │ │ ├── ManagedConnectionHandler.java
│ │ │ ├── ManagedXASessionHandler.java
│ │ │ ├── jdbc/
│ │ │ │ ├── CallableStatementImpl.java
│ │ │ │ ├── ConnectionImpl.java
│ │ │ │ ├── DatabaseMetaDataImpl.java
│ │ │ │ ├── PreparedStatementImpl.java
│ │ │ │ ├── StatementImpl.java
│ │ │ │ ├── XAConnectionImpl.java
│ │ │ │ └── XADataSourceImpl.java
│ │ │ └── properties/
│ │ │ ├── ConnectorResourcePropertySource.java
│ │ │ └── ConnectorResourcePropertySourceFactory.java
│ │ ├── rpc/
│ │ │ ├── TransactionInterceptorImpl.java
│ │ │ ├── TransactionRequestImpl.java
│ │ │ └── TransactionResponseImpl.java
│ │ ├── serialize/
│ │ │ └── XAResourceDeserializerImpl.java
│ │ └── spring/
│ │ ├── ManagedConnectionFactoryPostProcessor.java
│ │ ├── TransactionBeanFactoryAutoInjector.java
│ │ └── TransactionDebuggablePostProcessor.java
│ └── resources/
│ ├── bytejta-supports-core.xml
│ ├── bytejta-supports-standalone.xml
│ └── bytejta-supports-task.xml
├── bytejta-supports-dubbo/
│ ├── pom.xml
│ └── src/
│ └── main/
│ ├── java/
│ │ └── org/
│ │ └── bytesoft/
│ │ └── bytejta/
│ │ └── supports/
│ │ └── dubbo/
│ │ ├── DubboRemoteCoordinator.java
│ │ ├── InvocationContextRegistry.java
│ │ ├── TransactionBeanRegistry.java
│ │ ├── config/
│ │ │ └── DubboSupportConfiguration.java
│ │ ├── ext/
│ │ │ └── ILoadBalancer.java
│ │ ├── internal/
│ │ │ ├── TransactionBeanConfigValidator.java
│ │ │ ├── TransactionEndpointAutoInjector.java
│ │ │ └── TransactionParticipantRegistrant.java
│ │ ├── serialize/
│ │ │ └── XAResourceDeserializerImpl.java
│ │ └── spi/
│ │ ├── TransactionLoadBalance.java
│ │ ├── TransactionLoadBalancer.java
│ │ └── TransactionServiceFilter.java
│ └── resources/
│ ├── META-INF/
│ │ └── dubbo/
│ │ ├── com.alibaba.dubbo.rpc.Filter
│ │ ├── com.alibaba.dubbo.rpc.cluster.LoadBalance
│ │ └── org.bytesoft.bytejta.supports.dubbo.ext.ILoadBalancer
│ └── bytejta-supports-dubbo.xml
├── bytejta-supports-springcloud/
│ ├── pom.xml
│ └── src/
│ └── main/
│ ├── java/
│ │ └── org/
│ │ └── bytesoft/
│ │ └── bytejta/
│ │ └── supports/
│ │ └── springcloud/
│ │ ├── SpringCloudBeanRegistry.java
│ │ ├── SpringCloudCoordinator.java
│ │ ├── SpringCloudEndpointPostProcessor.java
│ │ ├── config/
│ │ │ └── SpringCloudConfiguration.java
│ │ ├── controller/
│ │ │ └── TransactionCoordinatorController.java
│ │ ├── dbcp/
│ │ │ └── CommonDBCPXADataSourceWrapper.java
│ │ ├── feign/
│ │ │ ├── TransactionClientRegistry.java
│ │ │ ├── TransactionFeignBeanPostProcessor.java
│ │ │ ├── TransactionFeignContract.java
│ │ │ ├── TransactionFeignDecoder.java
│ │ │ ├── TransactionFeignErrorDecoder.java
│ │ │ ├── TransactionFeignHandler.java
│ │ │ └── TransactionFeignInterceptor.java
│ │ ├── hystrix/
│ │ │ ├── TransactionHystrixBeanPostProcessor.java
│ │ │ ├── TransactionHystrixFallbackFactoryHandler.java
│ │ │ ├── TransactionHystrixFallbackHandler.java
│ │ │ ├── TransactionHystrixFeignHandler.java
│ │ │ ├── TransactionHystrixInvocation.java
│ │ │ ├── TransactionHystrixInvocationHandler.java
│ │ │ └── TransactionHystrixMethodHandler.java
│ │ ├── loadbalancer/
│ │ │ ├── TransactionLoadBalancerInterceptor.java
│ │ │ └── TransactionLoadBalancerRuleImpl.java
│ │ ├── property/
│ │ │ ├── TransactionPropertySource.java
│ │ │ └── TransactionPropertySourceFactory.java
│ │ ├── rule/
│ │ │ ├── TransactionRule.java
│ │ │ └── TransactionRuleImpl.java
│ │ ├── serialize/
│ │ │ └── XAResourceDeserializerImpl.java
│ │ └── web/
│ │ ├── TransactionHandlerInterceptor.java
│ │ └── TransactionRequestInterceptor.java
│ └── resources/
│ └── bytejta-supports-springcloud.xml
└── pom.xml
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: ['https://raw.githubusercontent.com/wiki/liuyangming/ByteTCC/resources/donation/liuyangming%40alipay.png', 'https://raw.githubusercontent.com/wiki/liuyangming/ByteTCC/resources/donation/liuyangming%40weixin.png'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
================================================
FILE: .gitignore
================================================
*.class
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
.settings
/bytejta-core/.classpath
/bytejta-core/.project
/bytejta-core/.gitignore
/bytejta-supports/.classpath
/bytejta-supports/.gitignore
/bytejta-supports/.project
target
/bytejta-supports-dubbo/.classpath
/bytejta-supports-dubbo/.project
/bytejta-supports-springcloud/.classpath
/bytejta-supports-springcloud/.project
/bytejta-logger/.classpath
/bytejta-logger/.project
/bytejta-transaction-logger/.classpath
/bytejta-transaction-logger/.project
================================================
FILE: LICENSE
================================================
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.
================================================
FILE: README-zh.md
================================================
ByteJTA是一个基于XA/2PC机制的分布式事务管理器。实现了JTA接口,可以很好的与EJB、Spring等容器(本文档下文说明中将以Spring容器为例)进行集成。
## 一、文档 & 样例
* 使用文档: https://github.com/liuyangming/ByteJTA/wiki
* 使用样例: https://github.com/liuyangming/ByteJTA-sample
## 二、ByteJTA特性
* 1、支持Spring容器的声明式事务管理;
* 2、支持多数据源、跨应用、跨服务器等分布式事务场景;
* 3、支持spring cloud;
* 4、支持dubbo服务框架;
## 三、建议及改进
若您有任何建议,可以通过1)加入qq群537445956/606453172向群主提出,或2)发送邮件至bytefox#126.com向我反馈。本人承诺,任何建议都将会被认真考虑,优秀的建议将会被采用,但不保证一定会在当前版本中实现。
================================================
FILE: README.md
================================================
**ByteJTA** is an implementation of Distributed Transaction Manager, based on the XA/2PC mechanism.
**ByteJTA** is comptible with JTA and could be seamlessly integrated with Spring and other Java containers.
## 1. Quick Start
#### 1.1 Add maven depenency
###### 1.1.1. Spring Cloud
```xml
org.bytesoftbytejta-supports-springcloud0.5.0-BETA9
```
###### 1.1.2. dubbo
```xml
org.bytesoftbytejta-supports-dubbo0.5.0-BETA9
```
## 2. Documentation & Samples
* [Document](https://github.com/liuyangming/ByteJTA/wiki)
* [Sample](https://github.com/liuyangming/ByteJTA-sample)
## 3. Features
* support declarative transaction management
* support distributed transaction scenarios. e.g. multi-datasource, cross-applications and cross-servers transaction
* support Dubbo framework
* support Spring Cloud
## 4. Contact Me
If you have any questions or comments regarding this project, please feel free to contact me at:
1. send mail to _[bytefox#126.com](bytefox@126.com)_
~OR~
2. add Tecent QQ group 537445956/606453172
We will review all the suggestions and implement good ones in future release.
================================================
FILE: bytejta-core/pom.xml
================================================
4.0.0org.bytesoftbytejta-parent0.5.0-BETA9bytejta-corejarbytejta-coreThe bytejta-core project is the core module of ByteJTA.http://www.bytesoft.orgUTF-8javax.transactionjavax.transaction-apijavax.jmsjavax.jms-apijavax.resourcejavax.resource-apiorg.slf4jslf4j-apiorg.apache.commonscommons-lang3commons-iocommons-iocom.cauchohessiancom.esotericsoftwarekryojavax.annotationjavax.annotation-apijavax.injectjavax.inject
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/bytejta/TransactionBeanFactoryImpl.java
================================================
/**
* Copyright 2014-2016 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.bytejta;
import org.bytesoft.transaction.TransactionBeanFactory;
import org.bytesoft.transaction.TransactionLock;
import org.bytesoft.transaction.TransactionManager;
import org.bytesoft.transaction.TransactionRecovery;
import org.bytesoft.transaction.TransactionRepository;
import org.bytesoft.transaction.logging.ArchiveDeserializer;
import org.bytesoft.transaction.logging.TransactionLogger;
import org.bytesoft.transaction.remote.RemoteCoordinator;
import org.bytesoft.transaction.supports.TransactionTimer;
import org.bytesoft.transaction.supports.rpc.TransactionInterceptor;
import org.bytesoft.transaction.supports.serialize.XAResourceDeserializer;
import org.bytesoft.transaction.xa.XidFactory;
public class TransactionBeanFactoryImpl implements TransactionBeanFactory {
private static final TransactionBeanFactoryImpl instance = new TransactionBeanFactoryImpl();
private TransactionManager transactionManager;
private XidFactory xidFactory;
private TransactionTimer transactionTimer;
private TransactionLogger transactionLogger;
private TransactionRepository transactionRepository;
private TransactionInterceptor transactionInterceptor;
private TransactionRecovery transactionRecovery;
private RemoteCoordinator transactionCoordinator;
private TransactionLock transactionLock;
private ArchiveDeserializer archiveDeserializer;
private XAResourceDeserializer resourceDeserializer;
private TransactionBeanFactoryImpl() {
if (instance != null) {
throw new IllegalStateException();
}
}
public static TransactionBeanFactoryImpl getInstance() {
return instance;
}
public TransactionManager getTransactionManager() {
return transactionManager;
}
public void setTransactionManager(TransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
public XidFactory getXidFactory() {
return xidFactory;
}
public void setXidFactory(XidFactory xidFactory) {
this.xidFactory = xidFactory;
}
public TransactionTimer getTransactionTimer() {
return transactionTimer;
}
public void setTransactionTimer(TransactionTimer transactionTimer) {
this.transactionTimer = transactionTimer;
}
public TransactionRepository getTransactionRepository() {
return transactionRepository;
}
public void setTransactionRepository(TransactionRepository transactionRepository) {
this.transactionRepository = transactionRepository;
}
public TransactionInterceptor getTransactionInterceptor() {
return transactionInterceptor;
}
public void setTransactionInterceptor(TransactionInterceptor transactionInterceptor) {
this.transactionInterceptor = transactionInterceptor;
}
public TransactionLock getTransactionLock() {
return transactionLock;
}
public void setTransactionLock(TransactionLock transactionLock) {
this.transactionLock = transactionLock;
}
public TransactionRecovery getTransactionRecovery() {
return transactionRecovery;
}
public void setTransactionRecovery(TransactionRecovery transactionRecovery) {
this.transactionRecovery = transactionRecovery;
}
public RemoteCoordinator getNativeParticipant() {
return transactionCoordinator;
}
public void setTransactionCoordinator(RemoteCoordinator remoteCoordinator) {
this.transactionCoordinator = remoteCoordinator;
}
public TransactionLogger getTransactionLogger() {
return transactionLogger;
}
public void setTransactionLogger(TransactionLogger transactionLogger) {
this.transactionLogger = transactionLogger;
}
public ArchiveDeserializer getArchiveDeserializer() {
return archiveDeserializer;
}
public void setArchiveDeserializer(ArchiveDeserializer archiveDeserializer) {
this.archiveDeserializer = archiveDeserializer;
}
public XAResourceDeserializer getResourceDeserializer() {
return resourceDeserializer;
}
public void setResourceDeserializer(XAResourceDeserializer resourceDeserializer) {
this.resourceDeserializer = resourceDeserializer;
}
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/bytejta/TransactionCoordinator.java
================================================
/**
* Copyright 2014-2016 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.bytejta;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.RollbackException;
import javax.transaction.Status;
import javax.transaction.SystemException;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import org.bytesoft.common.utils.ByteUtils;
import org.bytesoft.common.utils.CommonUtils;
import org.bytesoft.transaction.CommitRequiredException;
import org.bytesoft.transaction.RollbackRequiredException;
import org.bytesoft.transaction.Transaction;
import org.bytesoft.transaction.TransactionBeanFactory;
import org.bytesoft.transaction.TransactionContext;
import org.bytesoft.transaction.TransactionException;
import org.bytesoft.transaction.TransactionManager;
import org.bytesoft.transaction.TransactionRepository;
import org.bytesoft.transaction.aware.TransactionBeanFactoryAware;
import org.bytesoft.transaction.aware.TransactionEndpointAware;
import org.bytesoft.transaction.remote.RemoteAddr;
import org.bytesoft.transaction.remote.RemoteCoordinator;
import org.bytesoft.transaction.remote.RemoteNode;
import org.bytesoft.transaction.xa.TransactionXid;
import org.bytesoft.transaction.xa.XidFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TransactionCoordinator implements RemoteCoordinator, TransactionBeanFactoryAware, TransactionEndpointAware {
static final Logger logger = LoggerFactory.getLogger(TransactionCoordinator.class);
@javax.inject.Inject
private TransactionBeanFactory beanFactory;
private String endpoint;
private transient boolean ready = false;
private final Lock lock = new ReentrantLock();
public Transaction getTransactionQuietly() {
TransactionManager transactionManager = this.beanFactory.getTransactionManager();
return transactionManager.getTransactionQuietly();
}
public Transaction start(TransactionContext transactionContext, int flags) throws XAException {
TransactionRepository transactionRepository = this.beanFactory.getTransactionRepository();
TransactionManager transactionManager = this.beanFactory.getTransactionManager();
if (transactionManager.getTransactionQuietly() != null) {
throw new XAException(XAException.XAER_PROTO);
}
TransactionXid globalXid = (TransactionXid) transactionContext.getXid();
Transaction transaction = null;
try {
transaction = transactionRepository.getTransaction(globalXid);
} catch (TransactionException tex) {
throw new XAException(XAException.XAER_RMERR);
}
if (transaction == null) {
transaction = new TransactionImpl(transactionContext);
((TransactionImpl) transaction).setBeanFactory(this.beanFactory);
long expired = transactionContext.getExpiredTime();
long current = System.currentTimeMillis();
long timeoutMillis = (expired - current) / 1000L;
transaction.setTransactionTimeout((int) timeoutMillis);
transactionRepository.putTransaction(globalXid, transaction);
logger.info("{}> begin-participant", ByteUtils.byteArrayToString(globalXid.getGlobalTransactionId()));
}
transactionManager.associateThread(transaction);
// this.transactionStatistic.fireBeginTransaction(transaction);
return transaction;
}
public Transaction end(TransactionContext transactionContext, int flags) throws XAException {
TransactionManager transactionManager = this.beanFactory.getTransactionManager();
return transactionManager.desociateThread();
}
/** supports resume only, for tcc transaction manager. */
public void start(Xid xid, int flags) throws XAException {
if (XAResource.TMRESUME != flags) {
throw new XAException(XAException.XAER_INVAL);
}
TransactionManager transactionManager = this.beanFactory.getTransactionManager();
XidFactory xidFactory = this.beanFactory.getXidFactory();
Transaction current = transactionManager.getTransactionQuietly();
if (current != null) {
throw new XAException(XAException.XAER_PROTO);
}
TransactionRepository transactionRepository = this.beanFactory.getTransactionRepository();
TransactionXid branchXid = (TransactionXid) xid;
TransactionXid globalXid = xidFactory.createGlobalXid(branchXid.getGlobalTransactionId());
Transaction transaction = null;
try {
transaction = transactionRepository.getTransaction(globalXid);
} catch (TransactionException tex) {
throw new XAException(XAException.XAER_RMERR);
}
if (transaction == null) {
throw new XAException(XAException.XAER_NOTA);
}
transactionManager.associateThread(transaction);
}
/** supports suspend only, for tcc transaction manager. */
public void end(Xid xid, int flags) throws XAException {
if (XAResource.TMSUSPEND != flags) {
throw new XAException(XAException.XAER_INVAL);
}
TransactionManager transactionManager = this.beanFactory.getTransactionManager();
XidFactory xidFactory = this.beanFactory.getXidFactory();
Transaction transaction = transactionManager.getTransactionQuietly();
if (transaction == null) {
throw new XAException(XAException.XAER_NOTA);
}
TransactionContext transactionContext = transaction.getTransactionContext();
TransactionXid transactionXid = transactionContext.getXid();
TransactionXid branchXid = (TransactionXid) xid;
TransactionXid globalXid = xidFactory.createGlobalXid(branchXid.getGlobalTransactionId());
if (CommonUtils.equals(globalXid, transactionXid) == false) {
throw new XAException(XAException.XAER_INVAL);
}
transactionManager.desociateThread();
}
public void commit(Xid xid, boolean onePhaseCommit) throws XAException {
this.checkParticipantReadyIfNecessary();
XidFactory xidFactory = this.beanFactory.getXidFactory();
TransactionXid branchXid = (TransactionXid) xid;
TransactionXid globalXid = xidFactory.createGlobalXid(branchXid.getGlobalTransactionId());
TransactionRepository repository = beanFactory.getTransactionRepository();
Transaction transaction = null;
try {
transaction = repository.getTransaction(globalXid);
} catch (TransactionException tex) {
throw new XAException(XAException.XAER_RMERR);
}
if (transaction == null) {
throw new XAException(XAException.XAER_NOTA);
}
if (onePhaseCommit) {
try {
this.beanFactory.getTransactionManager().associateThread(transaction);
transaction.fireBeforeTransactionCompletion();
this.beanFactory.getTransactionTimer().stopTiming(transaction);
} catch (RollbackRequiredException rrex) {
this.rollback(xid);
XAException xaex = new XAException(XAException.XA_HEURRB);
xaex.initCause(rrex);
throw xaex;
} catch (SystemException ex) {
this.rollback(xid);
XAException xaex = new XAException(XAException.XA_HEURRB);
xaex.initCause(ex);
throw xaex;
} catch (RuntimeException rex) {
this.rollback(xid);
XAException xaex = new XAException(XAException.XA_HEURRB);
xaex.initCause(rex);
throw xaex;
} finally {
this.beanFactory.getTransactionManager().desociateThread();
}
} // end-if (onePhaseCommit)
try {
transaction.participantCommit(onePhaseCommit);
transaction.forgetQuietly(); // forget transaction
} catch (SecurityException ex) {
logger.error("{}> Error occurred while committing remote coordinator.",
ByteUtils.byteArrayToString(xid.getGlobalTransactionId()), ex);
repository.putErrorTransaction(globalXid, transaction);
XAException xaex = new XAException(XAException.XAER_RMERR);
xaex.initCause(ex);
throw xaex;
} catch (CommitRequiredException ex) {
logger.error("{}> Error occurred while committing remote coordinator.",
ByteUtils.byteArrayToString(xid.getGlobalTransactionId()), ex);
repository.putErrorTransaction(globalXid, transaction);
XAException xaex = new XAException(XAException.XAER_RMERR);
xaex.initCause(ex);
throw xaex;
} catch (RollbackException ex) {
logger.error("{}> Error occurred while committing remote coordinator, tx has been rolled back.",
ByteUtils.byteArrayToString(xid.getGlobalTransactionId()), ex);
// don't forget if branch-transaction has been hueristic completed.
repository.putErrorTransaction(globalXid, transaction);
XAException xaex = new XAException(XAException.XA_HEURRB);
xaex.initCause(ex);
throw xaex;
} catch (HeuristicMixedException ex) {
logger.error("{}> Error occurred while committing remote coordinator, tx has been completed mixed.",
ByteUtils.byteArrayToString(xid.getGlobalTransactionId()), ex);
// don't forget if branch-transaction has been hueristic completed.
repository.putErrorTransaction(globalXid, transaction);
XAException xaex = new XAException(XAException.XA_HEURMIX);
xaex.initCause(ex);
throw xaex;
} catch (HeuristicRollbackException ex) {
logger.error("{}> Error occurred while committing remote coordinator, tx has been rolled back heuristically.",
ByteUtils.byteArrayToString(xid.getGlobalTransactionId()), ex);
// don't forget if branch-transaction has been hueristic completed.
repository.putErrorTransaction(globalXid, transaction);
XAException xaex = new XAException(XAException.XA_HEURRB);
xaex.initCause(ex);
throw xaex;
} catch (SystemException ex) {
logger.error("{}> Error occurred while committing remote coordinator.",
ByteUtils.byteArrayToString(xid.getGlobalTransactionId()), ex);
repository.putErrorTransaction(globalXid, transaction);
XAException xaex = new XAException(XAException.XAER_RMERR);
xaex.initCause(ex);
throw xaex;
} catch (RuntimeException ex) {
logger.error("{}> Error occurred while committing remote coordinator.",
ByteUtils.byteArrayToString(xid.getGlobalTransactionId()), ex);
repository.putErrorTransaction(globalXid, transaction);
XAException xaex = new XAException(XAException.XAER_RMERR);
xaex.initCause(ex);
throw xaex;
} finally {
transaction.fireAfterTransactionCompletion();
}
}
public void forgetQuietly(Xid xid) {
try {
this.forget(xid);
} catch (XAException ex) {
switch (ex.errorCode) {
case XAException.XAER_NOTA:
break;
default:
logger.error("{}> Error occurred while forgeting remote coordinator.",
ByteUtils.byteArrayToString(xid.getGlobalTransactionId()), ex);
}
} catch (RuntimeException ex) {
logger.error("{}> Error occurred while forgeting remote coordinator.",
ByteUtils.byteArrayToString(xid.getGlobalTransactionId()), ex);
}
}
public void forget(Xid xid) throws XAException {
this.checkParticipantReadyIfNecessary();
if (xid == null) {
throw new XAException(XAException.XAER_INVAL);
}
XidFactory xidFactory = this.beanFactory.getXidFactory();
TransactionXid branchXid = (TransactionXid) xid;
TransactionXid globalXid = xidFactory.createGlobalXid(branchXid.getGlobalTransactionId());
TransactionRepository transactionRepository = beanFactory.getTransactionRepository();
Transaction transaction = null;
try {
transaction = transactionRepository.getErrorTransaction(globalXid);
} catch (TransactionException tex) {
throw new XAException(XAException.XAER_RMERR);
}
if (transaction == null) {
throw new XAException(XAException.XAER_NOTA);
}
try {
transaction.forget();
} catch (SystemException ex) {
logger.error("{}> Error occurred while forgeting remote coordinator.",
ByteUtils.byteArrayToString(xid.getGlobalTransactionId()), ex);
throw new XAException(XAException.XAER_RMERR);
} catch (RuntimeException rex) {
logger.error("{}> Error occurred while forgeting remote coordinator.",
ByteUtils.byteArrayToString(xid.getGlobalTransactionId()), rex);
throw new XAException(XAException.XAER_RMERR);
}
}
public int getTransactionTimeout() throws XAException {
return 0;
}
public boolean isSameRM(XAResource xares) throws XAException {
throw new XAException(XAException.XAER_RMERR);
}
public int prepare(Xid xid) throws XAException {
this.checkParticipantReadyIfNecessary();
XidFactory xidFactory = this.beanFactory.getXidFactory();
TransactionXid branchXid = (TransactionXid) xid;
TransactionXid globalXid = xidFactory.createGlobalXid(branchXid.getGlobalTransactionId());
TransactionRepository repository = beanFactory.getTransactionRepository();
Transaction transaction = null;
try {
transaction = repository.getTransaction(globalXid);
} catch (TransactionException tex) {
throw new XAException(XAException.XAER_RMERR);
}
if (transaction == null) {
throw new XAException(XAException.XAER_NOTA);
}
try {
this.beanFactory.getTransactionManager().associateThread(transaction);
transaction.fireBeforeTransactionCompletion();
this.beanFactory.getTransactionTimer().stopTiming(transaction);
} catch (RollbackRequiredException rrex) {
throw new XAException(XAException.XAER_RMERR);
} catch (SystemException ex) {
throw new XAException(XAException.XAER_RMERR);
} catch (RuntimeException rex) {
throw new XAException(XAException.XAER_RMERR);
} finally {
this.beanFactory.getTransactionManager().desociateThread();
}
int participantVote = XAResource.XA_OK;
try {
participantVote = transaction.participantPrepare();
} catch (CommitRequiredException crex) {
participantVote = XAResource.XA_OK;
} catch (RollbackRequiredException rrex) {
throw new XAException(XAException.XAER_RMERR);
} finally {
if (participantVote == XAResource.XA_RDONLY) {
transaction.fireAfterTransactionCompletion();
} // end-if (participantVote == XAResource.XA_RDONLY)
}
return participantVote;
}
public Xid[] recover(int flag) throws XAException {
this.checkParticipantReadyIfNecessary();
TransactionRepository repository = beanFactory.getTransactionRepository();
List allTransactionList = repository.getActiveTransactionList();
List transactions = new ArrayList();
for (int i = 0; i < allTransactionList.size(); i++) {
Transaction transaction = allTransactionList.get(i);
int transactionStatus = transaction.getTransactionStatus();
if (transactionStatus == Status.STATUS_PREPARED || transactionStatus == Status.STATUS_COMMITTING
|| transactionStatus == Status.STATUS_ROLLING_BACK || transactionStatus == Status.STATUS_COMMITTED
|| transactionStatus == Status.STATUS_ROLLEDBACK) {
transactions.add(transaction);
} else if (transaction.getTransactionContext().isRecoveried()) {
transactions.add(transaction);
}
}
TransactionXid[] xidArray = new TransactionXid[transactions.size()];
for (int i = 0; i < transactions.size(); i++) {
Transaction transaction = transactions.get(i);
xidArray[i] = transaction.getTransactionContext().getXid();
}
return xidArray;
}
public void rollback(Xid xid) throws XAException {
this.checkParticipantReadyIfNecessary();
XidFactory xidFactory = this.beanFactory.getXidFactory();
TransactionXid branchXid = (TransactionXid) xid;
TransactionXid globalXid = xidFactory.createGlobalXid(branchXid.getGlobalTransactionId());
TransactionRepository repository = beanFactory.getTransactionRepository();
Transaction transaction = null;
try {
transaction = repository.getTransaction(globalXid);
} catch (TransactionException tex) {
throw new XAException(XAException.XAER_RMERR);
}
if (transaction == null) {
throw new XAException(XAException.XAER_NOTA);
}
try {
this.beanFactory.getTransactionManager().associateThread(transaction);
transaction.fireBeforeTransactionCompletionQuietly();
this.beanFactory.getTransactionManager().desociateThread();
this.beanFactory.getTransactionTimer().stopTiming(transaction);
transaction.participantRollback();
transaction.forgetQuietly(); // forget transaction
} catch (RollbackRequiredException rrex) {
logger.error("{}> Error occurred while rolling back remote coordinator.",
ByteUtils.byteArrayToString(xid.getGlobalTransactionId()), rrex);
repository.putErrorTransaction(globalXid, transaction);
XAException xaex = new XAException(XAException.XAER_RMERR);
xaex.initCause(rrex);
throw xaex;
} catch (SystemException ex) {
logger.error("{}> Error occurred while rolling back remote coordinator.",
ByteUtils.byteArrayToString(xid.getGlobalTransactionId()), ex);
repository.putErrorTransaction(globalXid, transaction);
XAException xaex = new XAException(XAException.XAER_RMERR);
xaex.initCause(ex);
throw xaex;
} catch (RuntimeException rrex) {
logger.error("{}> Error occurred while rolling back remote coordinator.",
ByteUtils.byteArrayToString(xid.getGlobalTransactionId()), rrex);
repository.putErrorTransaction(globalXid, transaction);
XAException xaex = new XAException(XAException.XAER_RMERR);
xaex.initCause(rrex);
throw xaex;
} finally {
transaction.fireAfterTransactionCompletion();
}
}
public void markParticipantReady() {
try {
this.lock.lock();
this.ready = true;
} finally {
this.lock.unlock();
}
}
private void checkParticipantReadyIfNecessary() throws XAException {
if (this.ready == false) {
this.checkParticipantReady();
}
}
private void checkParticipantReady() throws XAException {
try {
this.lock.lock();
if (this.ready == false) {
throw new XAException(XAException.XAER_RMFAIL);
}
} finally {
this.lock.unlock();
}
}
public boolean setTransactionTimeout(int seconds) throws XAException {
return false;
}
public String getEndpoint() {
return endpoint;
}
public void setEndpoint(String identifier) {
this.endpoint = identifier;
}
public RemoteAddr getRemoteAddr() {
return CommonUtils.getRemoteAddr(this.endpoint);
}
public RemoteNode getRemoteNode() {
return CommonUtils.getRemoteNode(this.endpoint);
}
public String getIdentifier() {
return this.endpoint;
}
public String getApplication() {
return CommonUtils.getApplication(this.endpoint);
}
public TransactionBeanFactory getBeanFactory() {
return this.beanFactory;
}
public void setBeanFactory(TransactionBeanFactory tbf) {
this.beanFactory = tbf;
}
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/bytejta/TransactionImpl.java
================================================
/**
* Copyright 2014-2016 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.bytejta;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.transaction.HeuristicCommitException;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.RollbackException;
import javax.transaction.Status;
import javax.transaction.Synchronization;
import javax.transaction.SystemException;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import org.apache.commons.lang3.StringUtils;
import org.bytesoft.bytejta.resource.XATerminatorImpl;
import org.bytesoft.bytejta.resource.XATerminatorOptd;
import org.bytesoft.bytejta.strategy.CommonTransactionStrategy;
import org.bytesoft.bytejta.strategy.LastResourceOptimizeStrategy;
import org.bytesoft.bytejta.strategy.SimpleTransactionStrategy;
import org.bytesoft.bytejta.strategy.VacantTransactionStrategy;
import org.bytesoft.bytejta.supports.jdbc.LocalXAResource;
import org.bytesoft.bytejta.supports.resource.CommonResourceDescriptor;
import org.bytesoft.bytejta.supports.resource.LocalXAResourceDescriptor;
import org.bytesoft.bytejta.supports.resource.RemoteResourceDescriptor;
import org.bytesoft.bytejta.supports.resource.UnidentifiedResourceDescriptor;
import org.bytesoft.common.utils.ByteUtils;
import org.bytesoft.common.utils.CommonUtils;
import org.bytesoft.transaction.CommitRequiredException;
import org.bytesoft.transaction.RollbackRequiredException;
import org.bytesoft.transaction.Transaction;
import org.bytesoft.transaction.TransactionBeanFactory;
import org.bytesoft.transaction.TransactionContext;
import org.bytesoft.transaction.TransactionRepository;
import org.bytesoft.transaction.archive.TransactionArchive;
import org.bytesoft.transaction.archive.XAResourceArchive;
import org.bytesoft.transaction.internal.SynchronizationList;
import org.bytesoft.transaction.internal.TransactionListenerList;
import org.bytesoft.transaction.internal.TransactionResourceListenerList;
import org.bytesoft.transaction.logging.TransactionLogger;
import org.bytesoft.transaction.remote.RemoteCoordinator;
import org.bytesoft.transaction.remote.RemoteSvc;
import org.bytesoft.transaction.supports.TransactionExtra;
import org.bytesoft.transaction.supports.TransactionListener;
import org.bytesoft.transaction.supports.TransactionResourceListener;
import org.bytesoft.transaction.supports.resource.XAResourceDescriptor;
import org.bytesoft.transaction.xa.TransactionXid;
import org.bytesoft.transaction.xa.XidFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TransactionImpl implements Transaction {
static final Logger logger = LoggerFactory.getLogger(TransactionImpl.class);
private transient boolean timing = true;
private TransactionBeanFactory beanFactory;
private TransactionStrategy transactionStrategy;
private int transactionStatus;
private int transactionTimeout;
private int transactionVote;
private TransactionExtra transactionalExtra;
private final TransactionContext transactionContext;
private final TransactionResourceListenerList resourceListenerList = new TransactionResourceListenerList();
private final Map remoteParticipantMap = new HashMap();
private final Map nativeParticipantMap = new HashMap();
private XAResourceArchive participant; // last resource
private final List participantList = new ArrayList();
private final List nativeParticipantList = new ArrayList();
private final List remoteParticipantList = new ArrayList();
private final SynchronizationList synchronizationList = new SynchronizationList();
private final TransactionListenerList transactionListenerList = new TransactionListenerList();
private transient Exception createdAt;
public TransactionImpl(TransactionContext txContext) {
this.transactionContext = txContext;
}
public synchronized int participantPrepare() throws RollbackRequiredException, CommitRequiredException {
if (this.transactionStatus == Status.STATUS_MARKED_ROLLBACK) {
throw new RollbackRequiredException();
} else if (this.transactionStatus == Status.STATUS_ROLLEDBACK) {
throw new RollbackRequiredException();
} else if (this.transactionStatus == Status.STATUS_ROLLING_BACK) {
throw new RollbackRequiredException();
} else if (this.transactionStatus == Status.STATUS_UNKNOWN) {
throw new RollbackRequiredException();
} else if (this.transactionStatus == Status.STATUS_NO_TRANSACTION) {
// it's impossible
throw new RollbackRequiredException();
} else if (this.transactionStatus == Status.STATUS_PREPARED) {
throw new CommitRequiredException();
} else if (this.transactionStatus == Status.STATUS_COMMITTING) {
throw new CommitRequiredException();
} else if (this.transactionStatus == Status.STATUS_COMMITTED) {
throw new CommitRequiredException();
} /* else active, preparing {} */
TransactionLogger transactionLogger = beanFactory.getTransactionLogger();
TransactionXid xid = this.transactionContext.getXid();
this.transactionStatus = Status.STATUS_PREPARING;
TransactionArchive archive = this.getTransactionArchive();
transactionLogger.createTransaction(archive);
this.transactionListenerList.onPrepareStart(xid);
logger.info("{}> prepare-participant start", ByteUtils.byteArrayToString(xid.getGlobalTransactionId()));
try {
TransactionStrategy currentStrategy = this.getTransactionStrategy();
int vote = currentStrategy.prepare(xid);
this.transactionStatus = Status.STATUS_PREPARED;
archive.setStatus(this.transactionStatus);
this.transactionVote = vote;
archive.setVote(vote);
this.transactionListenerList.onPrepareSuccess(xid);
logger.info("{}> prepare-participant complete successfully",
ByteUtils.byteArrayToString(xid.getGlobalTransactionId()));
return vote;
} catch (CommitRequiredException crex) {
this.transactionVote = XAResource.XA_OK;
archive.setVote(this.transactionVote);
this.transactionStatus = Status.STATUS_COMMITTING;
archive.setStatus(this.transactionStatus);
this.transactionListenerList.onPrepareSuccess(xid);
logger.info("{}> prepare-participant complete successfully",
ByteUtils.byteArrayToString(xid.getGlobalTransactionId()));
throw crex;
} catch (RollbackRequiredException rrex) {
this.transactionStatus = Status.STATUS_ROLLING_BACK;
archive.setStatus(this.transactionStatus);
this.transactionListenerList.onPrepareFailure(xid);
logger.info("{}> prepare-participant failed", ByteUtils.byteArrayToString(xid.getGlobalTransactionId()));
throw rrex;
} catch (RuntimeException xaex) {
this.transactionStatus = Status.STATUS_ROLLING_BACK;
archive.setStatus(this.transactionStatus);
this.transactionListenerList.onPrepareFailure(xid);
logger.info("{}> prepare-participant failed", ByteUtils.byteArrayToString(xid.getGlobalTransactionId()));
RollbackRequiredException rrex = new RollbackRequiredException();
rrex.initCause(xaex);
throw rrex;
} finally {
transactionLogger.updateTransaction(archive);
}
}
public synchronized void recoveryCommit() throws CommitRequiredException, SystemException {
TransactionXid xid = this.transactionContext.getXid();
try {
this.recoverIfNecessary(); // Recover if transaction is recovered from tx-log.
this.transactionContext.setRecoveredTimes(this.transactionContext.getRecoveredTimes() + 1);
this.transactionContext.setCreatedTime(System.currentTimeMillis());
this.invokeParticipantCommit(false);
} catch (HeuristicMixedException ex) {
logger.error("{}> recover: branch={}, status= mixed, message= {}",
ByteUtils.byteArrayToString(xid.getGlobalTransactionId()),
ByteUtils.byteArrayToString(xid.getBranchQualifier()), ex.getMessage(), ex);
SystemException sysEx = new SystemException();
sysEx.initCause(ex);
throw sysEx;
} catch (HeuristicRollbackException ex) {
logger.error("{}> recover: branch={}, status= rolledback",
ByteUtils.byteArrayToString(xid.getGlobalTransactionId()),
ByteUtils.byteArrayToString(xid.getBranchQualifier()), ex);
SystemException sysEx = new SystemException();
sysEx.initCause(ex);
throw sysEx;
}
}
/* opc: true, compensable-transaction & remote-coordinator; false, remote-coordinator */
public synchronized void participantCommit(boolean opc) throws RollbackException, HeuristicMixedException,
HeuristicRollbackException, SecurityException, IllegalStateException, CommitRequiredException, SystemException {
if (this.transactionContext.isRecoveried()) {
this.recover(); // Execute recoveryInit if transaction is recovered from tx-log.
this.invokeParticipantCommit(opc);
return;
} // end-if (this.transactionContext.isRecoveried())
Transaction transaction = //
Transaction.class.isInstance(this.transactionalExtra) ? (Transaction) this.transactionalExtra : null;
TransactionContext transactionContext = transaction == null ? null : transaction.getTransactionContext();
TransactionXid transactionXid = transactionContext == null ? null : transactionContext.getXid();
boolean compensable = transactionXid != null && XidFactory.TCC_FORMAT_ID == transactionXid.getFormatId();
if (compensable) {
this.compensableOnePhaseCommit();
} else if (opc) {
this.participantOnePhaseCommit();
} else {
this.participantTwoPhaseCommit();
}
}
private void checkForTransactionExtraIfNecessary() throws RollbackException, HeuristicMixedException,
HeuristicRollbackException, SecurityException, IllegalStateException, CommitRequiredException, SystemException {
if (this.transactionalExtra != null) /* for ByteTCC */ {
if (this.participantList.isEmpty() == false && this.participant == null) /* see initGetTransactionStrategy */ {
this.participantRollback();
throw new HeuristicRollbackException();
} else if (this.participantList.size() > 1) {
this.participantRollback();
throw new HeuristicRollbackException();
}
} // end-if (this.transactionalExtra != null)
}
private void compensableOnePhaseCommit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException,
SecurityException, IllegalStateException, CommitRequiredException, SystemException {
this.checkForTransactionExtraIfNecessary();
if (this.transactionStatus == Status.STATUS_MARKED_ROLLBACK) {
this.participantRollback();
throw new HeuristicRollbackException();
} else if (this.transactionStatus == Status.STATUS_ROLLING_BACK) {
throw new HeuristicMixedException();
} else if (this.transactionStatus == Status.STATUS_ROLLEDBACK) {
throw new HeuristicRollbackException();
} else if (this.transactionStatus == Status.STATUS_UNKNOWN) {
throw new IllegalStateException();
} else if (this.transactionStatus == Status.STATUS_NO_TRANSACTION) {
throw new IllegalStateException();
} else if (this.transactionStatus == Status.STATUS_COMMITTED) {
return;
} /* else active, preparing, prepared, committing {} */
TransactionXid xid = this.transactionContext.getXid();
try {
this.transactionStatus = Status.STATUS_COMMITTING;
TransactionArchive archive = this.getTransactionArchive();
this.transactionListenerList.onCommitStart(xid);
logger.info("{}> commit-participant start", ByteUtils.byteArrayToString(xid.getGlobalTransactionId()));
// transactionLogger.updateTransaction(archive); // unneccessary
TransactionStrategy currentStrategy = this.getTransactionStrategy();
currentStrategy.commit(xid, true);
this.transactionStatus = Status.STATUS_COMMITTED; // Status.STATUS_COMMITTED;
archive.setStatus(this.transactionStatus);
this.transactionListenerList.onCommitSuccess(xid);
logger.info("{}> commit-participant complete successfully",
ByteUtils.byteArrayToString(xid.getGlobalTransactionId()));
// transactionLogger.updateTransaction(archive); // unneccessary
} catch (HeuristicMixedException ex) {
this.transactionListenerList.onCommitHeuristicMixed(xid); // should never happen
throw ex;
} catch (HeuristicRollbackException ex) {
this.transactionListenerList.onCommitHeuristicRolledback(xid);
throw ex;
} catch (SystemException ex) {
this.transactionListenerList.onCommitFailure(xid);
throw ex;
} catch (RuntimeException ex) {
this.transactionListenerList.onCommitFailure(xid);
SystemException error = new SystemException();
error.initCause(ex);
throw error;
}
}
private void participantOnePhaseCommit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException,
SecurityException, IllegalStateException, CommitRequiredException, SystemException {
this.checkForTransactionExtraIfNecessary();
if (this.transactionStatus == Status.STATUS_MARKED_ROLLBACK) {
this.participantRollback();
throw new HeuristicRollbackException();
} else if (this.transactionStatus == Status.STATUS_ROLLING_BACK) {
throw new HeuristicMixedException();
} else if (this.transactionStatus == Status.STATUS_ROLLEDBACK) {
throw new HeuristicRollbackException();
} else if (this.transactionStatus == Status.STATUS_UNKNOWN) {
throw new IllegalStateException();
} else if (this.transactionStatus == Status.STATUS_NO_TRANSACTION) {
throw new IllegalStateException();
} else if (this.transactionStatus == Status.STATUS_COMMITTED) {
return;
} /* else active, preparing, prepared, committing {} */
try {
try {
this.invokeParticipantPrepare();
} catch (CommitRequiredException crex) {
/* some RMs has already been committed. */
}
this.invokeParticipantCommit(true);
} catch (RollbackRequiredException rrex) {
this.participantRollback();
HeuristicRollbackException hrex = new HeuristicRollbackException();
hrex.initCause(rrex);
throw hrex;
} catch (SystemException ex) {
this.participantRollback();
HeuristicRollbackException hrex = new HeuristicRollbackException();
hrex.initCause(ex);
throw hrex;
} catch (RuntimeException rex) {
this.participantRollback();
HeuristicRollbackException hrex = new HeuristicRollbackException();
hrex.initCause(rex);
throw hrex;
}
}
private void participantTwoPhaseCommit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException,
SecurityException, IllegalStateException, CommitRequiredException, SystemException {
if (this.transactionStatus == Status.STATUS_ACTIVE) {
throw new IllegalStateException();
} else if (this.transactionStatus == Status.STATUS_MARKED_ROLLBACK) {
this.participantRollback();
throw new HeuristicRollbackException();
} else if (this.transactionStatus == Status.STATUS_ROLLING_BACK) {
throw new HeuristicMixedException();
} else if (this.transactionStatus == Status.STATUS_ROLLEDBACK) {
throw new HeuristicRollbackException();
} else if (this.transactionStatus == Status.STATUS_UNKNOWN) {
throw new IllegalStateException();
} else if (this.transactionStatus == Status.STATUS_NO_TRANSACTION) {
throw new IllegalStateException();
} else if (this.transactionStatus == Status.STATUS_COMMITTED) {
return;
} /* else preparing, prepared, committing {} */
try {
this.invokeParticipantCommit(false);
} catch (RollbackRequiredException rrex) {
this.participantRollback();
HeuristicRollbackException hrex = new HeuristicRollbackException();
hrex.initCause(rrex);
throw hrex;
} catch (SystemException ex) {
this.participantRollback();
HeuristicRollbackException hrex = new HeuristicRollbackException();
hrex.initCause(ex);
throw hrex;
} catch (RuntimeException rex) {
this.participantRollback();
HeuristicRollbackException hrex = new HeuristicRollbackException();
hrex.initCause(rex);
throw hrex;
}
}
private void invokeParticipantPrepare() throws RollbackRequiredException, CommitRequiredException {
TransactionLogger transactionLogger = beanFactory.getTransactionLogger();
TransactionXid xid = this.transactionContext.getXid();
logger.info("{}> prepare-participant start", ByteUtils.byteArrayToString(xid.getGlobalTransactionId()));
this.transactionStatus = Status.STATUS_PREPARING;
TransactionArchive archive = this.getTransactionArchive();
this.transactionListenerList.onPrepareStart(xid);
transactionLogger.createTransaction(archive); // transactionLogger.updateTransaction(archive);
CommitRequiredException commitRequired = null;
try {
TransactionStrategy currentStrategy = this.getTransactionStrategy();
currentStrategy.prepare(xid);
} catch (CommitRequiredException ex) {
commitRequired = ex;
} catch (RollbackRequiredException ex) {
this.transactionListenerList.onPrepareFailure(xid);
logger.info("{}> prepare-participant failed", ByteUtils.byteArrayToString(xid.getGlobalTransactionId()));
throw ex;
} catch (RuntimeException ex) {
this.transactionListenerList.onPrepareFailure(xid);
logger.info("{}> prepare-participant failed", ByteUtils.byteArrayToString(xid.getGlobalTransactionId()));
throw ex;
}
this.transactionStatus = Status.STATUS_PREPARED;
archive.setStatus(this.transactionStatus);
this.transactionListenerList.onPrepareSuccess(xid);
transactionLogger.updateTransaction(archive);
logger.info("{}> prepare-participant complete successfully", ByteUtils.byteArrayToString(xid.getGlobalTransactionId()));
if (commitRequired != null) {
throw commitRequired;
} // end-if (commitRequired != null)
}
private void invokeParticipantCommit(boolean onePhaseCommit)
throws HeuristicMixedException, HeuristicRollbackException, SystemException {
TransactionLogger transactionLogger = beanFactory.getTransactionLogger();
TransactionXid xid = this.transactionContext.getXid();
logger.info("{}> commit-participant start", ByteUtils.byteArrayToString(xid.getGlobalTransactionId()));
this.transactionStatus = Status.STATUS_COMMITTING;
TransactionArchive archive = this.getTransactionArchive();
this.transactionListenerList.onCommitStart(xid);
transactionLogger.updateTransaction(archive);
boolean unFinishExists = true;
try {
TransactionStrategy currentStrategy = this.getTransactionStrategy();
currentStrategy.commit(xid, onePhaseCommit);
unFinishExists = false;
} catch (HeuristicMixedException ex) {
this.transactionListenerList.onCommitHeuristicMixed(xid);
throw ex;
} catch (HeuristicRollbackException ex) {
this.transactionListenerList.onCommitHeuristicRolledback(xid);
throw ex;
} catch (SystemException ex) {
this.transactionListenerList.onCommitFailure(xid);
throw ex;
} catch (RuntimeException ex) {
this.transactionListenerList.onCommitFailure(xid);
throw ex;
} finally {
if (unFinishExists == false) {
this.transactionStatus = Status.STATUS_COMMITTED; // Status.STATUS_COMMITTED;
archive.setStatus(this.transactionStatus);
this.transactionListenerList.onCommitSuccess(xid);
transactionLogger.updateTransaction(archive);
logger.info("{}> commit-participant complete successfully",
ByteUtils.byteArrayToString(xid.getGlobalTransactionId()));
}
}
}
public synchronized void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException,
SecurityException, IllegalStateException, CommitRequiredException, SystemException {
if (this.transactionStatus == Status.STATUS_ACTIVE) {
this.fireCommit();
} else if (this.transactionStatus == Status.STATUS_MARKED_ROLLBACK) {
this.fireRollback();
throw new HeuristicRollbackException();
} else if (this.transactionStatus == Status.STATUS_ROLLEDBACK) /* should never happen */ {
throw new RollbackException();
} else if (this.transactionStatus == Status.STATUS_COMMITTED) /* should never happen */ {
logger.debug("Current transaction has already been committed.");
} else {
throw new IllegalStateException();
}
}
private void fireCommit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException,
IllegalStateException, CommitRequiredException, SystemException {
TransactionXid xid = this.transactionContext.getXid();
logger.info("{}> commit-transaction start", ByteUtils.byteArrayToString(xid.getGlobalTransactionId()));
if (this.participantList.size() == 0) {
this.skipOnePhaseCommit();
} else if (this.participantList.size() == 1 && (this.nativeParticipantList.size() == 1 || this.participant != null)) {
this.fireOnePhaseCommit();
} else {
this.fireTwoPhaseCommit();
}
logger.info("{}> commit-transaction complete successfully", ByteUtils.byteArrayToString(xid.getGlobalTransactionId()));
}
public synchronized void skipOnePhaseCommit()
throws HeuristicRollbackException, HeuristicMixedException, CommitRequiredException, SystemException {
TransactionXid xid = this.transactionContext.getXid();
this.transactionListenerList.onCommitStart(xid);
this.transactionListenerList.onCommitSuccess(xid);
}
public synchronized void fireOnePhaseCommit()
throws HeuristicRollbackException, HeuristicMixedException, CommitRequiredException, SystemException {
XAResourceArchive archive = null;
if (this.nativeParticipantList.size() > 0) {
archive = this.nativeParticipantList.get(0);
} else if (this.remoteParticipantList.size() > 0) {
archive = this.remoteParticipantList.get(0);
} else {
archive = this.participant;
}
TransactionXid xid = this.transactionContext.getXid();
try {
this.transactionListenerList.onCommitStart(xid);
archive.commit(xid, true);
this.transactionListenerList.onCommitSuccess(xid);
} catch (XAException xaex) {
switch (xaex.errorCode) {
case XAException.XA_HEURMIX:
this.transactionListenerList.onCommitHeuristicMixed(xid);
HeuristicMixedException hmex = new HeuristicMixedException();
hmex.initCause(xaex);
throw hmex;
case XAException.XA_HEURCOM:
this.transactionListenerList.onCommitSuccess(xid);
break;
case XAException.XA_HEURRB:
this.transactionListenerList.onCommitHeuristicRolledback(xid);
HeuristicRollbackException hrex = new HeuristicRollbackException();
hrex.initCause(xaex);
throw hrex;
default:
this.transactionListenerList.onCommitFailure(xid);
SystemException ex = new SystemException();
ex.initCause(xaex);
throw ex;
}
} catch (RuntimeException rex) {
this.transactionListenerList.onCommitFailure(xid);
SystemException sysEx = new SystemException();
sysEx.initCause(rex);
throw sysEx;
}
}
public synchronized void fireTwoPhaseCommit()
throws HeuristicRollbackException, HeuristicMixedException, CommitRequiredException, SystemException {
TransactionLogger transactionLogger = beanFactory.getTransactionLogger();
TransactionXid xid = this.transactionContext.getXid();
logger.info("{}> prepare-participant start", ByteUtils.byteArrayToString(xid.getGlobalTransactionId()));
this.transactionStatus = Status.STATUS_PREPARING;// .setStatusPreparing();
TransactionArchive archive = this.getTransactionArchive();// new TransactionArchive();
transactionLogger.createTransaction(archive);
this.transactionListenerList.onPrepareStart(xid);
TransactionStrategy currentStrategy = this.getTransactionStrategy();
int vote = XAResource.XA_RDONLY;
try {
vote = currentStrategy.prepare(xid);
} catch (RollbackRequiredException xaex) {
this.transactionListenerList.onPrepareFailure(xid);
logger.info("{}> prepare-participant failed", ByteUtils.byteArrayToString(xid.getGlobalTransactionId()));
this.invokeParticipantRollback(); // this.fireRollback();
HeuristicRollbackException hrex = new HeuristicRollbackException();
hrex.initCause(xaex);
throw hrex;
} catch (CommitRequiredException xaex) {
vote = XAResource.XA_OK;
// committed = true;
} catch (RuntimeException rex) {
this.transactionListenerList.onPrepareFailure(xid);
logger.info("{}> prepare-participant failed", ByteUtils.byteArrayToString(xid.getGlobalTransactionId()));
this.invokeParticipantRollback(); // this.fireRollback();
HeuristicRollbackException hrex = new HeuristicRollbackException();
hrex.initCause(rex);
throw hrex;
}
this.transactionListenerList.onPrepareSuccess(xid);
if (vote == XAResource.XA_RDONLY) {
this.transactionStatus = Status.STATUS_PREPARED;// .setStatusPrepared();
this.transactionVote = XAResource.XA_RDONLY;
archive.setVote(XAResource.XA_RDONLY);
archive.setStatus(this.transactionStatus);
this.transactionListenerList.onCommitStart(xid);
this.transactionListenerList.onCommitSuccess(xid);
logger.info("{}> prepare-participant & commit-participant complete successfully",
ByteUtils.byteArrayToString(xid.getGlobalTransactionId()));
transactionLogger.updateTransaction(archive);
} else {
// this.transactionStatus = Status.STATUS_PREPARED;// .setStatusPrepared();
logger.info("{}> prepare-participant complete successfully, and commit-participant start",
ByteUtils.byteArrayToString(xid.getGlobalTransactionId()));
this.transactionStatus = Status.STATUS_COMMITTING;// .setStatusCommiting();
this.transactionVote = XAResource.XA_OK;
archive.setVote(this.transactionVote);
archive.setStatus(this.transactionStatus);
this.transactionListenerList.onCommitStart(xid);
transactionLogger.updateTransaction(archive);
try {
currentStrategy.commit(xid, false);
} catch (HeuristicMixedException ex) {
this.transactionListenerList.onCommitHeuristicMixed(xid);
throw ex;
} catch (HeuristicRollbackException ex) {
this.transactionListenerList.onCommitHeuristicRolledback(xid);
throw ex;
} catch (SystemException ex) {
this.transactionListenerList.onCommitFailure(xid);
throw ex;
} catch (RuntimeException ex) {
this.transactionListenerList.onCommitFailure(xid);
throw ex;
}
this.transactionStatus = Status.STATUS_COMMITTED; // Status.STATUS_COMMITTED;
archive.setStatus(this.transactionStatus);
this.transactionListenerList.onCommitSuccess(xid);
transactionLogger.updateTransaction(archive);
logger.info("{}> commit-participant complete successfully",
ByteUtils.byteArrayToString(xid.getGlobalTransactionId()));
} // end-else-if (vote == XAResource.XA_RDONLY)
}
public synchronized boolean delistResource(XAResource xaRes, int flag) throws IllegalStateException, SystemException {
if (this.transactionStatus != Status.STATUS_ACTIVE && this.transactionStatus != Status.STATUS_MARKED_ROLLBACK) {
throw new IllegalStateException();
}
if (XAResourceDescriptor.class.isInstance(xaRes)) {
return this.delistResource((XAResourceDescriptor) xaRes, flag);
} else {
XAResourceDescriptor descriptor = new UnidentifiedResourceDescriptor();
((UnidentifiedResourceDescriptor) descriptor).setDelegate(xaRes);
((UnidentifiedResourceDescriptor) descriptor).setIdentifier("");
return this.delistResource(descriptor, flag);
}
}
public boolean delistResource(XAResourceDescriptor descriptor, int flag) throws IllegalStateException, SystemException {
RemoteCoordinator transactionCoordinator = (RemoteCoordinator) this.beanFactory.getNativeParticipant();
if (RemoteResourceDescriptor.class.isInstance(descriptor)) {
RemoteSvc nativeSvc = CommonUtils.getRemoteSvc(transactionCoordinator.getIdentifier());
RemoteSvc parentSvc = CommonUtils.getRemoteSvc(String.valueOf(this.transactionContext.getPropagatedBy()));
RemoteSvc remoteSvc = ((RemoteResourceDescriptor) descriptor).getRemoteSvc();
boolean nativeFlag = StringUtils.equalsIgnoreCase(remoteSvc.getServiceKey(), nativeSvc.getServiceKey());
boolean parentFlag = StringUtils.equalsIgnoreCase(remoteSvc.getServiceKey(), parentSvc.getServiceKey());
if (nativeFlag || parentFlag) {
return true;
}
}
XAResourceArchive archive = this.getEnlistedResourceArchive(descriptor);
if (archive == null) {
throw new SystemException();
}
try {
return this.delistResource(archive, flag);
} finally {
this.resourceListenerList.onDelistResource(archive.getXid(), descriptor);
}
}
private boolean delistResource(XAResourceArchive archive, int flag) throws SystemException {
try {
Xid branchXid = archive.getXid();
logger.info("{}> delist: xares= {}, branch= {}, flags= {}",
ByteUtils.byteArrayToString(branchXid.getGlobalTransactionId()), archive,
ByteUtils.byteArrayToString(branchXid.getBranchQualifier()), flag);
switch (flag) {
case XAResource.TMSUSPEND:
archive.end(branchXid, flag);
archive.setDelisted(true);
archive.setSuspended(true);
return true;
case XAResource.TMFAIL:
this.setRollbackOnlyQuietly();
case XAResource.TMSUCCESS:
archive.end(branchXid, flag);
archive.setDelisted(true);
return true;
default:
return false;
}
} catch (XAException xae) {
logger.error("XATerminatorImpl.delistResource(XAResourceArchive, int)", xae);
// Possible XAException values are XAER_RMERR, XAER_RMFAIL,
// XAER_NOTA, XAER_INVAL, XAER_PROTO, or XA_RB*.
switch (xae.errorCode) {
case XAException.XAER_NOTA:
// The specified XID is not known by the resource manager.
case XAException.XAER_INVAL:
// Invalid arguments were specified.
case XAException.XAER_PROTO:
// The routine was invoked in an improper context.
return false;
case XAException.XAER_RMFAIL:
// An error occurred that makes the resource manager unavailable.
case XAException.XAER_RMERR:
// An error occurred in dissociating the transaction branch from the thread of control.
return false; // throw new SystemException();
default /* XA_RB* */ :
return false; // throw new RollbackRequiredException();
}
} catch (RuntimeException ex) {
logger.error("XATerminatorImpl.delistResource(XAResourceArchive, int)", ex);
throw new SystemException();
}
}
public synchronized boolean enlistResource(XAResource xaRes)
throws RollbackException, IllegalStateException, SystemException {
if (this.transactionStatus == Status.STATUS_MARKED_ROLLBACK) {
// When a RollbackException is received, DBCP treats the state as STATUS_ROLLEDBACK,
// but the actual state is still STATUS_MARKED_ROLLBACK.
throw new IllegalStateException(); // throw new RollbackException();
} else if (this.transactionStatus != Status.STATUS_ACTIVE) {
throw new IllegalStateException();
}
if (XAResourceDescriptor.class.isInstance(xaRes)) {
return this.enlistResource((XAResourceDescriptor) xaRes);
} else if (XAResourceDescriptor.class.isInstance(xaRes) == false && this.transactionContext.isCoordinator()) {
XAResourceDescriptor descriptor = new UnidentifiedResourceDescriptor();
((UnidentifiedResourceDescriptor) descriptor).setIdentifier("");
((UnidentifiedResourceDescriptor) descriptor).setDelegate(xaRes);
return this.enlistResource(descriptor);
} else {
throw new SystemException("Unknown xa resource!");
}
}
private XAResourceArchive getEnlistedResourceArchive(XAResourceDescriptor descriptor) {
if (RemoteResourceDescriptor.class.isInstance(descriptor)) {
RemoteSvc remoteSvc = CommonUtils.getRemoteSvc(descriptor.getIdentifier()); // dubbo: old identifier
return this.remoteParticipantMap.get(remoteSvc);
} else {
return this.nativeParticipantMap.get(descriptor.getIdentifier());
}
}
private void putEnlistedResourceArchive(XAResourceArchive archive) {
XAResourceDescriptor descriptor = archive.getDescriptor();
String identifier = descriptor.getIdentifier();
if (RemoteResourceDescriptor.class.isInstance(descriptor)) {
RemoteSvc remoteSvc = CommonUtils.getRemoteSvc(identifier);
this.remoteParticipantMap.put(remoteSvc, archive);
} else {
this.nativeParticipantMap.put(identifier, archive);
}
}
public boolean enlistResource(XAResourceDescriptor descriptor)
throws RollbackException, IllegalStateException, SystemException {
XAResourceArchive archive = null;
XAResourceArchive enlisted = this.getEnlistedResourceArchive(descriptor);
if (enlisted != null) {
XAResourceDescriptor xard = enlisted.getDescriptor();
try {
archive = xard.isSameRM(descriptor) ? enlisted : archive;
} catch (Exception ex) {
logger.debug(ex.getMessage(), ex);
}
}
int flags = XAResource.TMNOFLAGS;
if (archive == null) {
archive = new XAResourceArchive();
archive.setDescriptor(descriptor);
archive.setIdentified(true);
if (this.transactionalExtra != null && this.transactionalExtra.getTransactionXid() != null) {
archive.setXid(this.transactionalExtra.getTransactionXid());
} else {
XidFactory xidFactory = this.beanFactory.getXidFactory();
archive.setXid(xidFactory.createBranchXid(this.transactionContext.getXid()));
}
} else {
flags = XAResource.TMJOIN;
}
descriptor.setTransactionTimeoutQuietly(this.transactionTimeout);
if (this.participant != null && (LocalXAResourceDescriptor.class.isInstance(descriptor)
|| UnidentifiedResourceDescriptor.class.isInstance(descriptor))) {
XAResourceDescriptor lro = this.participant.getDescriptor();
try {
if (lro.isSameRM(descriptor) == false) {
throw new SystemException("Only one non-XA resource is allowed to participate in global transaction.");
}
} catch (XAException ex) {
SystemException sysEx = new SystemException();
sysEx.initCause(ex);
throw sysEx;
} catch (RuntimeException ex) {
SystemException sysEx = new SystemException();
sysEx.initCause(ex);
throw sysEx;
}
}
boolean success = false;
try {
Boolean enlistValue = this.enlistResource(archive, flags);
success = enlistValue != null && enlistValue;
return enlistValue != null;
} finally {
if (success) {
String identifier = descriptor.getIdentifier(); // dubbo: new identifier
boolean resourceValid = true;
if (CommonResourceDescriptor.class.isInstance(descriptor)) {
this.nativeParticipantList.add(archive);
} else if (RemoteResourceDescriptor.class.isInstance(descriptor)) {
RemoteCoordinator transactionCoordinator = (RemoteCoordinator) this.beanFactory.getNativeParticipant();
RemoteSvc nativeSvc = CommonUtils.getRemoteSvc(transactionCoordinator.getIdentifier());
RemoteSvc parentSvc = CommonUtils.getRemoteSvc(String.valueOf(this.transactionContext.getPropagatedBy()));
RemoteSvc remoteSvc = ((RemoteResourceDescriptor) descriptor).getRemoteSvc();
boolean nativeFlag = StringUtils.equalsIgnoreCase(remoteSvc.getServiceKey(), nativeSvc.getServiceKey());
boolean parentFlag = StringUtils.equalsIgnoreCase(remoteSvc.getServiceKey(), parentSvc.getServiceKey());
if (nativeFlag || parentFlag) {
logger.warn("Endpoint {} can not be its own remote branch!", identifier);
return false;
}
this.remoteParticipantList.add(archive);
this.putEnlistedResourceArchive(archive);
} else if (this.participant == null) {
// this.participant = this.participant == null ? archive : this.participant;
this.participant = archive;
} else {
throw new SystemException("There already has a local-resource exists!");
}
if (resourceValid) {
this.participantList.add(archive);
this.putEnlistedResourceArchive(archive);
this.resourceListenerList.onEnlistResource(archive.getXid(), descriptor);
} // end-if (resourceValid)
}
}
}
// result description:
// 1) true, success(current resource should be added to archive list);
// 2) false, success(resource has already been added to archive list);
// 3) null, failure.
private Boolean enlistResource(XAResourceArchive archive, int flag) throws SystemException {
try {
Xid branchXid = archive.getXid();
logger.info("{}> enlist: xares= {}, branch= {}, flags: {}",
ByteUtils.byteArrayToString(branchXid.getGlobalTransactionId()), archive,
ByteUtils.byteArrayToString(branchXid.getBranchQualifier()), flag);
switch (flag) {
case XAResource.TMNOFLAGS:
long expired = this.transactionContext.getExpiredTime();
long current = System.currentTimeMillis();
long remains = expired - current;
int timeout = (int) (remains / 1000L);
archive.setTransactionTimeout(timeout);
archive.start(branchXid, flag);
return true;
case XAResource.TMJOIN:
archive.start(branchXid, flag);
archive.setDelisted(false);
return false;
case XAResource.TMRESUME:
archive.start(branchXid, flag);
archive.setDelisted(false);
archive.setSuspended(false);
return false;
default:
return null;
}
} catch (XAException xae) {
logger.error("XATerminatorImpl.enlistResource(XAResourceArchive, int)", xae);
// Possible exceptions are XA_RB*, XAER_RMERR, XAER_RMFAIL,
// XAER_DUPID, XAER_OUTSIDE, XAER_NOTA, XAER_INVAL, or XAER_PROTO.
switch (xae.errorCode) {
case XAException.XAER_DUPID:
// * If neither TMJOIN nor TMRESUME is specified and the transaction
// * specified by xid has previously been seen by the resource manager,
// * the resource manager throws the XAException exception with XAER_DUPID error code.
return null;
case XAException.XAER_OUTSIDE:
// The resource manager is doing work outside any global transaction
// on behalf of the application.
case XAException.XAER_NOTA:
// Either TMRESUME or TMJOIN was set inflags, and the specified XID is not
// known by the resource manager.
case XAException.XAER_INVAL:
// Invalid arguments were specified.
case XAException.XAER_PROTO:
// The routine was invoked in an improper context.
return null;
case XAException.XAER_RMFAIL:
// An error occurred that makes the resource manager unavailable
case XAException.XAER_RMERR:
// An error occurred in associating the transaction branch with the thread of control
return null;
default /* XA_RB **/ :
// When a RollbackException is received, DBCP treats the state as STATUS_ROLLEDBACK,
// but the actual state is still STATUS_MARKED_ROLLBACK.
return null; // throw new RollbackException();
}
} catch (RuntimeException ex) {
logger.error("XATerminatorImpl.enlistResource(XAResourceArchive, int)", ex);
throw new SystemException();
}
}
public int getStatus() /* throws SystemException */ {
return this.transactionStatus;
}
public synchronized void registerSynchronization(Synchronization sync)
throws RollbackException, IllegalStateException, SystemException {
if (this.transactionStatus == Status.STATUS_MARKED_ROLLBACK) {
throw new RollbackException();
} else if (this.transactionStatus == Status.STATUS_ACTIVE) {
this.synchronizationList.registerSynchronizationQuietly(sync);
logger.debug("{}> register-sync: sync= {}"//
, ByteUtils.byteArrayToString(this.transactionContext.getXid().getGlobalTransactionId()), sync);
} else {
throw new IllegalStateException();
}
}
public synchronized void rollback() throws IllegalStateException, RollbackRequiredException, SystemException {
if (this.transactionStatus == Status.STATUS_UNKNOWN) {
throw new IllegalStateException();
} else if (this.transactionStatus == Status.STATUS_NO_TRANSACTION) {
throw new IllegalStateException();
} else if (this.transactionStatus == Status.STATUS_COMMITTED) /* should never happen */ {
throw new IllegalStateException();
} else if (this.transactionStatus == Status.STATUS_ROLLEDBACK) /* should never happen */ {
logger.debug("Current transaction has already been rolled back.");
} else {
this.fireRollback();
}
}
private void fireRollback() throws IllegalStateException, RollbackRequiredException, SystemException {
TransactionXid xid = this.transactionContext.getXid();
logger.info("{}> rollback-transaction start", ByteUtils.byteArrayToString(xid.getGlobalTransactionId()));
this.invokeParticipantRollback();
logger.info("{}> rollback-transaction complete successfully",
ByteUtils.byteArrayToString(xid.getGlobalTransactionId()));
}
public synchronized void recoveryRollback() throws RollbackRequiredException, SystemException {
this.recoverIfNecessary(); // Recover if transaction is recovered from tx-log.
this.transactionContext.setRecoveredTimes(this.transactionContext.getRecoveredTimes() + 1);
this.transactionContext.setCreatedTime(System.currentTimeMillis());
this.invokeParticipantRollback();
}
public synchronized void participantRollback() throws IllegalStateException, RollbackRequiredException, SystemException {
if (this.transactionStatus == Status.STATUS_UNKNOWN) {
throw new IllegalStateException();
} else if (this.transactionStatus == Status.STATUS_NO_TRANSACTION) {
throw new IllegalStateException();
} else if (this.transactionStatus == Status.STATUS_COMMITTED) {
throw new IllegalStateException();
} else if (this.transactionStatus == Status.STATUS_ROLLEDBACK) {
return;
}
if (this.transactionContext.isRecoveried()) {
this.recover(); // Execute recoveryInit if transaction is recovered from tx-log.
this.invokeParticipantRollback();
} else {
this.invokeParticipantRollback();
}
}
private void invokeParticipantRollback() throws SystemException {
TransactionLogger transactionLogger = beanFactory.getTransactionLogger();
TransactionXid xid = this.transactionContext.getXid();
logger.info("{}> rollback-participant start", ByteUtils.byteArrayToString(xid.getGlobalTransactionId()));
this.transactionStatus = Status.STATUS_ROLLING_BACK;
TransactionArchive archive = this.getTransactionArchive();
this.transactionListenerList.onRollbackStart(xid);
transactionLogger.updateTransaction(archive); // don't create!
try {
TransactionStrategy currentStrategy = this.getTransactionStrategy();
currentStrategy.rollback(xid);
} catch (HeuristicMixedException ex) {
this.transactionListenerList.onRollbackFailure(xid);
SystemException sysEx = new SystemException();
sysEx.initCause(ex);
throw sysEx;
} catch (HeuristicCommitException ex) {
this.transactionListenerList.onRollbackFailure(xid);
SystemException sysEx = new SystemException();
sysEx.initCause(ex);
throw sysEx;
} catch (SystemException ex) {
this.transactionListenerList.onRollbackFailure(xid);
throw ex;
} catch (RuntimeException ex) {
this.transactionListenerList.onRollbackFailure(xid);
SystemException sysEx = new SystemException();
sysEx.initCause(ex);
throw sysEx;
}
this.transactionStatus = Status.STATUS_ROLLEDBACK; // Status.STATUS_ROLLEDBACK;
archive.setStatus(this.transactionStatus);
this.transactionListenerList.onRollbackSuccess(xid);
transactionLogger.updateTransaction(archive);
logger.info("{}> rollback-participant complete successfully",
ByteUtils.byteArrayToString(xid.getGlobalTransactionId()));
}
public void suspend() throws RollbackRequiredException, SystemException {
boolean rollbackRequired = false;
boolean errorExists = false;
for (int i = 0; i < this.participantList.size(); i++) {
XAResourceArchive xares = this.participantList.get(i);
if (xares.isDelisted() == false) {
try {
this.delistResource(xares, XAResource.TMSUSPEND);
} catch (RollbackRequiredException ex) {
rollbackRequired = true;
} catch (SystemException ex) {
errorExists = true;
} catch (RuntimeException ex) {
errorExists = true;
}
}
}
if (rollbackRequired) {
this.setRollbackOnlyQuietly();
throw new RollbackRequiredException();
} else if (errorExists) {
throw new SystemException(XAException.XAER_RMERR);
}
}
public void resume() throws RollbackRequiredException, SystemException {
// boolean rollbackRequired = false;
boolean errorExists = false;
for (int i = 0; i < this.participantList.size(); i++) {
XAResourceArchive xares = this.participantList.get(i);
if (xares.isDelisted()) {
try {
this.enlistResource(xares, XAResource.TMRESUME);
} catch (SystemException rex) {
errorExists = true;
} catch (RuntimeException rex) {
errorExists = true;
}
}
}
if (errorExists) {
throw new SystemException(XAException.XAER_RMERR);
}
}
public synchronized void fireBeforeTransactionCompletionQuietly() {
this.synchronizationList.beforeCompletion();
this.delistAllResourceQuietly();
}
public synchronized void fireBeforeTransactionCompletion() throws RollbackRequiredException, SystemException {
this.synchronizationList.beforeCompletion();
this.delistAllResource();
}
public synchronized void fireAfterTransactionCompletion() {
this.synchronizationList.afterCompletion(this.transactionStatus);
}
public void delistAllResourceQuietly() {
try {
this.delistAllResource();
} catch (RollbackRequiredException rrex) {
logger.warn(rrex.getMessage(), rrex);
} catch (SystemException ex) {
logger.warn(ex.getMessage(), ex);
} catch (RuntimeException rex) {
logger.warn(rex.getMessage(), rex);
}
}
private void delistAllResource() throws RollbackRequiredException, SystemException {
boolean rollbackRequired = false;
boolean errorExists = false;
for (int i = 0; i < this.participantList.size(); i++) {
XAResourceArchive xares = this.participantList.get(i);
if (this.transactionContext.isRecoveried()) {
continue;
} // end-if (this.transactionContext.isRecoveried())
if (xares.isDelisted() == false) {
try {
this.delistResource(xares, XAResource.TMSUCCESS);
} catch (RollbackRequiredException ex) {
rollbackRequired = true;
} catch (SystemException ex) {
errorExists = true;
} catch (RuntimeException ex) {
errorExists = true;
} finally {
this.resourceListenerList.onDelistResource(xares.getXid(), xares.getDescriptor());
}
}
} // end-for
if (rollbackRequired) {
throw new RollbackRequiredException();
} else if (errorExists) {
throw new SystemException(XAException.XAER_RMERR);
}
}
public boolean isMarkedRollbackOnly() {
return this.transactionContext.isRollbackOnly();
}
public void setRollbackOnlyQuietly() {
try {
this.setRollbackOnly();
} catch (Exception ex) {
logger.debug(ex.getMessage(), ex);
}
}
public synchronized void setRollbackOnly() throws IllegalStateException, SystemException {
if (this.transactionStatus == Status.STATUS_ACTIVE || this.transactionStatus == Status.STATUS_MARKED_ROLLBACK) {
this.transactionContext.setRollbackOnly(true);
this.transactionStatus = Status.STATUS_MARKED_ROLLBACK;
} else {
throw new IllegalStateException();
}
}
public void recoverIfNecessary() throws SystemException {
if (this.transactionContext.isRecoveried()) {
this.recover();
}
}
public synchronized void recover() throws SystemException {
if (transactionStatus == Status.STATUS_PREPARING) {
this.recover4PreparingStatus();
} else if (transactionStatus == Status.STATUS_COMMITTING) {
this.recover4CommittingStatus();
} else if (transactionStatus == Status.STATUS_ROLLING_BACK) {
this.recover4RollingBackStatus();
}
}
public void recover4PreparingStatus() throws SystemException {
TransactionLogger transactionLogger = this.beanFactory.getTransactionLogger();
boolean unPrepareExists = false;
for (int i = 0; i < this.participantList.size(); i++) {
XAResourceArchive archive = this.participantList.get(i);
boolean prepareFlag = archive.getVote() != XAResourceArchive.DEFAULT_VOTE;
boolean preparedVal = archive.isReadonly() || prepareFlag;
if (archive.isRecovered()) {
unPrepareExists = preparedVal ? unPrepareExists : true;
continue;
} else if (preparedVal) {
continue;
}
boolean xidExists = this.recover(archive);
unPrepareExists = xidExists ? false : true;
}
if (unPrepareExists == false) {
this.transactionStatus = Status.STATUS_PREPARED;
TransactionArchive archive = this.getTransactionArchive();
transactionLogger.updateTransaction(archive);
}
}
public void recover4CommittingStatus() throws SystemException {
TransactionLogger transactionLogger = this.beanFactory.getTransactionLogger();
boolean rollbackExists = false;
boolean unCommitExists = false;
for (int i = 0; i < this.participantList.size(); i++) {
XAResourceArchive archive = this.participantList.get(i);
XAResourceDescriptor descriptor = archive.getDescriptor();
XAResource delegate = descriptor.getDelegate();
boolean localFlag = LocalXAResource.class.isInstance(delegate);
if (localFlag //
&& LastResourceOptimizeStrategy.class.isInstance(this.transactionStrategy)) {
throw new SystemException();
}
if (archive.isRecovered()) {
unCommitExists = archive.isCommitted() ? unCommitExists : true;
continue;
} else if (archive.isCommitted()) {
continue;
}
boolean xidExists = this.recover(archive);
if (localFlag) {
rollbackExists = xidExists ? rollbackExists : true;
} else {
unCommitExists = xidExists ? true : unCommitExists;
}
}
if (rollbackExists) {
this.transactionStatus = Status.STATUS_ROLLING_BACK;
TransactionArchive archive = this.getTransactionArchive();
transactionLogger.updateTransaction(archive);
} else if (unCommitExists == false) {
this.transactionStatus = Status.STATUS_COMMITTED;
TransactionArchive archive = this.getTransactionArchive();
transactionLogger.updateTransaction(archive);
}
}
public void recover4RollingBackStatus() throws SystemException {
TransactionLogger transactionLogger = this.beanFactory.getTransactionLogger();
boolean unRollbackExists = false;
for (int i = 0; i < this.participantList.size(); i++) {
XAResourceArchive archive = this.participantList.get(i);
if (archive.isRecovered()) {
unRollbackExists = archive.isRolledback() ? unRollbackExists : true;
continue;
} else if (archive.isRolledback()) {
continue;
}
boolean xidExists = this.recover(archive);
unRollbackExists = xidExists ? true : unRollbackExists;
}
if (unRollbackExists == false) {
this.transactionStatus = Status.STATUS_ROLLEDBACK;
TransactionArchive archive = this.getTransactionArchive();
transactionLogger.updateTransaction(archive);
}
}
private boolean recover(XAResourceArchive archive) throws SystemException {
TransactionXid globalXid = this.transactionContext.getXid();
boolean xidRecovered = false;
XAResourceDescriptor descriptor = archive.getDescriptor();
XAResource delegate = descriptor.getDelegate();
boolean nativeFlag = LocalXAResource.class.isInstance(delegate);
boolean remoteFlag = RemoteCoordinator.class.isInstance(delegate);
if (nativeFlag) {
try {
((LocalXAResource) delegate).recoverable(archive.getXid());
xidRecovered = true;
} catch (XAException ex) {
switch (ex.errorCode) {
case XAException.XAER_NOTA:
break;
default:
logger.error("{}> recover-resource failed. branch= {}",
ByteUtils.byteArrayToString(globalXid.getGlobalTransactionId()),
ByteUtils.byteArrayToString(globalXid.getBranchQualifier()), ex);
throw new SystemException();
}
}
} else if (archive.isIdentified()) {
Xid thisXid = archive.getXid();
byte[] thisGlobalTransactionId = thisXid.getGlobalTransactionId();
byte[] thisBranchQualifier = thisXid.getBranchQualifier();
try {
Xid[] array = archive.recover(XAResource.TMSTARTRSCAN | XAResource.TMENDRSCAN);
for (int j = 0; xidRecovered == false && array != null && j < array.length; j++) {
Xid thatXid = array[j];
byte[] thatGlobalTransactionId = thatXid.getGlobalTransactionId();
byte[] thatBranchQualifier = thatXid.getBranchQualifier();
boolean formatIdEquals = thisXid.getFormatId() == thatXid.getFormatId();
boolean transactionIdEquals = Arrays.equals(thisGlobalTransactionId, thatGlobalTransactionId);
boolean qualifierEquals = Arrays.equals(thisBranchQualifier, thatBranchQualifier);
xidRecovered = formatIdEquals && transactionIdEquals && (remoteFlag || qualifierEquals);
}
} catch (Exception ex) {
logger.error("{}> recover-resource failed. branch= {}",
ByteUtils.byteArrayToString(globalXid.getGlobalTransactionId()),
ByteUtils.byteArrayToString(globalXid.getBranchQualifier()), ex);
throw new SystemException();
}
}
archive.setRecovered(true);
return xidRecovered;
}
public synchronized void forgetQuietly() {
TransactionXid xid = this.transactionContext.getXid();
try {
this.forget();
} catch (SystemException ex) {
logger.error("Error occurred while forgetting transaction: {}",
ByteUtils.byteArrayToInt(xid.getGlobalTransactionId()), ex);
} catch (RuntimeException ex) {
logger.error("Error occurred while forgetting transaction: {}",
ByteUtils.byteArrayToInt(xid.getGlobalTransactionId()), ex);
}
}
public synchronized void forget() throws SystemException {
TransactionRepository repository = beanFactory.getTransactionRepository();
TransactionLogger transactionLogger = this.beanFactory.getTransactionLogger();
TransactionXid xid = this.transactionContext.getXid();
this.cleanup(); // forget branch-transaction has been hueristic completed.
repository.removeErrorTransaction(xid);
repository.removeTransaction(xid);
transactionLogger.deleteTransaction(this.getTransactionArchive());
}
public synchronized void cleanup() throws SystemException {
boolean unFinishExists = false;
for (int i = 0; i < this.participantList.size(); i++) {
XAResourceArchive archive = this.participantList.get(i);
Xid currentXid = archive.getXid();
if (archive.isHeuristic()) {
try {
Xid branchXid = archive.getXid();
archive.forget(branchXid);
} catch (XAException xae) {
// Possible exception values are XAER_RMERR, XAER_RMFAIL
// , XAER_NOTA, XAER_INVAL, or XAER_PROTO.
switch (xae.errorCode) {
case XAException.XAER_RMERR:
unFinishExists = true;
logger.error("{}> forget: xares= {}, branch={}, error= {}",
ByteUtils.byteArrayToString(currentXid.getGlobalTransactionId()), archive,
ByteUtils.byteArrayToString(currentXid.getBranchQualifier()), xae.errorCode);
break;
case XAException.XAER_RMFAIL:
unFinishExists = true;
logger.error("{}> forget: xares= {}, branch={}, error= {}",
ByteUtils.byteArrayToString(currentXid.getGlobalTransactionId()), archive,
ByteUtils.byteArrayToString(currentXid.getBranchQualifier()), xae.errorCode);
break;
case XAException.XAER_NOTA:
case XAException.XAER_INVAL:
case XAException.XAER_PROTO:
break;
default:
unFinishExists = true;
logger.error("{}> forget: xares= {}, branch={}, error= {}",
ByteUtils.byteArrayToString(currentXid.getGlobalTransactionId()), archive,
ByteUtils.byteArrayToString(currentXid.getBranchQualifier()), xae.errorCode);
}
}
} // end-if
} // end-for
if (unFinishExists) {
throw new SystemException("Error occurred while cleaning branch transaction!");
}
}
public TransactionArchive getTransactionArchive() {
TransactionArchive transactionArchive = new TransactionArchive();
transactionArchive.setVote(this.transactionVote);
transactionArchive.setXid(this.transactionContext.getXid());
transactionArchive.setCoordinator(this.transactionContext.isCoordinator());
transactionArchive.setOptimizedResource(this.participant);
transactionArchive.getNativeResources().addAll(this.nativeParticipantList);
transactionArchive.getRemoteResources().addAll(this.remoteParticipantList);
transactionArchive.setStatus(this.transactionStatus);
// transactionArchive.setPropagated(this.transactionContext.isPropagated());
transactionArchive.setPropagatedBy(this.transactionContext.getPropagatedBy());
TransactionStrategy currentStrategy = this.getTransactionStrategy();
if (CommonTransactionStrategy.class.isInstance(currentStrategy)) {
transactionArchive.setTransactionStrategyType(TransactionStrategy.TRANSACTION_STRATEGY_COMMON);
} else if (SimpleTransactionStrategy.class.isInstance(currentStrategy)) {
transactionArchive.setTransactionStrategyType(TransactionStrategy.TRANSACTION_STRATEGY_SIMPLE);
} else if (LastResourceOptimizeStrategy.class.isInstance(currentStrategy)) {
transactionArchive.setTransactionStrategyType(TransactionStrategy.TRANSACTION_STRATEGY_LRO);
} else {
transactionArchive.setTransactionStrategyType(TransactionStrategy.TRANSACTION_STRATEGY_VACANT);
}
return transactionArchive;
}
public int hashCode() {
TransactionXid transactionXid = this.transactionContext == null ? null : this.transactionContext.getXid();
return transactionXid == null ? 0 : Arrays.hashCode(transactionXid.getGlobalTransactionId());
}
public boolean equals(Object obj) {
if (obj == null) {
return false;
} else if (TransactionImpl.class.equals(obj.getClass()) == false) {
return false;
}
TransactionImpl that = (TransactionImpl) obj;
TransactionContext thisContext = this.transactionContext;
TransactionContext thatContext = that.transactionContext;
TransactionXid thisXid = thisContext == null ? null : thisContext.getXid();
TransactionXid thatXid = thatContext == null ? null : thatContext.getXid();
return thisXid == null || thatXid == null ? false
: Arrays.equals(thisXid.getGlobalTransactionId(), thatXid.getGlobalTransactionId());
}
public void registerTransactionListener(TransactionListener listener) {
this.transactionListenerList.registerTransactionListener(listener);
}
public void registerTransactionResourceListener(TransactionResourceListener listener) {
this.resourceListenerList.registerTransactionResourceListener(listener);
}
public synchronized void stopTiming() {
this.setTiming(false);
}
public synchronized void changeTransactionTimeout(int timeout) {
long created = this.transactionContext.getCreatedTime();
transactionContext.setExpiredTime(created + timeout);
}
public TransactionStrategy getTransactionStrategy() {
TransactionStrategy strategy = //
this.transactionStrategy == null ? this.initGetTransactionStrategy() : this.transactionStrategy;
if (Status.STATUS_ACTIVE == this.transactionStatus || Status.STATUS_MARKED_ROLLBACK == this.transactionStatus) {
return strategy;
} else {
return this.transactionStrategy = this.transactionStrategy == null ? strategy : this.transactionStrategy;
}
}
private TransactionStrategy initGetTransactionStrategy() {
int nativeResNum = this.nativeParticipantList.size();
int remoteResNum = this.remoteParticipantList.size();
TransactionStrategy transactionStrategy = null;
if (this.participantList.isEmpty()) {
transactionStrategy = new VacantTransactionStrategy();
} else if (this.participant == null) /* TODO: LRO */ {
XATerminatorImpl nativeTerminator = new XATerminatorImpl();
nativeTerminator.setBeanFactory(this.beanFactory);
nativeTerminator.getResourceArchives().addAll(this.nativeParticipantList);
XATerminatorImpl remoteTerminator = new XATerminatorImpl();
remoteTerminator.setBeanFactory(this.beanFactory);
remoteTerminator.getResourceArchives().addAll(this.remoteParticipantList);
if (nativeResNum == 0) {
transactionStrategy = new SimpleTransactionStrategy(remoteTerminator);
} else if (remoteResNum == 0) {
transactionStrategy = new SimpleTransactionStrategy(nativeTerminator);
} else {
transactionStrategy = new CommonTransactionStrategy(nativeTerminator, remoteTerminator);
}
} else {
XATerminatorOptd terminatorOne = new XATerminatorOptd();
terminatorOne.setBeanFactory(this.beanFactory);
terminatorOne.getResourceArchives().add(this.participant);
XATerminatorImpl terminatorTwo = new XATerminatorImpl();
terminatorTwo.setBeanFactory(this.beanFactory);
terminatorTwo.getResourceArchives().addAll(this.nativeParticipantList);
terminatorTwo.getResourceArchives().addAll(this.remoteParticipantList);
int resNumber = nativeResNum + remoteResNum;
if (resNumber == 0) {
transactionStrategy = new SimpleTransactionStrategy(terminatorOne);
} else {
transactionStrategy = new LastResourceOptimizeStrategy(terminatorOne, terminatorTwo);
}
}
return transactionStrategy;
}
public void recoverTransactionStrategy(int transactionStrategyType) {
int nativeResNum = this.nativeParticipantList.size();
int remoteResNum = this.remoteParticipantList.size();
XATerminatorImpl nativeTerminator = new XATerminatorImpl();
nativeTerminator.setBeanFactory(this.beanFactory);
nativeTerminator.getResourceArchives().addAll(this.nativeParticipantList);
XATerminatorImpl remoteTerminator = new XATerminatorImpl();
remoteTerminator.setBeanFactory(this.beanFactory);
remoteTerminator.getResourceArchives().addAll(this.remoteParticipantList);
if (TransactionStrategy.TRANSACTION_STRATEGY_COMMON == transactionStrategyType) {
if (this.participant != null) {
throw new IllegalStateException();
} else if (nativeResNum == 0 || remoteResNum == 0) {
throw new IllegalStateException();
}
this.transactionStrategy = new CommonTransactionStrategy(nativeTerminator, remoteTerminator);
} else if (TransactionStrategy.TRANSACTION_STRATEGY_SIMPLE == transactionStrategyType) {
if (this.participant == null) {
if (nativeResNum > 0 && remoteResNum > 0) {
throw new IllegalStateException();
} else if (nativeResNum == 0 && remoteResNum == 0) {
throw new IllegalStateException();
} else if (nativeResNum == 0) {
this.transactionStrategy = new SimpleTransactionStrategy(remoteTerminator);
} else {
this.transactionStrategy = new SimpleTransactionStrategy(nativeTerminator);
}
} else {
int resNumber = nativeResNum + remoteResNum;
if (resNumber > 0) {
throw new IllegalStateException();
}
XATerminatorOptd terminatorOne = new XATerminatorOptd();
terminatorOne.setBeanFactory(this.beanFactory);
terminatorOne.getResourceArchives().add(this.participant);
this.transactionStrategy = new SimpleTransactionStrategy(terminatorOne);
}
} else if (TransactionStrategy.TRANSACTION_STRATEGY_LRO == transactionStrategyType) {
if (this.participant == null) {
throw new IllegalStateException();
}
XATerminatorOptd terminatorOne = new XATerminatorOptd();
terminatorOne.setBeanFactory(this.beanFactory);
terminatorOne.getResourceArchives().add(this.participant);
XATerminatorImpl terminatorTwo = new XATerminatorImpl();
terminatorTwo.setBeanFactory(this.beanFactory);
terminatorTwo.getResourceArchives().addAll(this.nativeParticipantList);
terminatorTwo.getResourceArchives().addAll(this.remoteParticipantList);
this.transactionStrategy = new LastResourceOptimizeStrategy(terminatorOne, terminatorTwo);
} else {
if (this.participant != null || nativeResNum > 0 || remoteResNum > 0) {
throw new IllegalStateException();
}
this.transactionStrategy = new VacantTransactionStrategy();
}
}
public XAResourceDescriptor getResourceDescriptor(String beanId) {
XAResourceArchive archive = this.nativeParticipantMap.get(beanId);
return archive == null ? null : archive.getDescriptor();
}
public XAResourceDescriptor getRemoteCoordinator(RemoteSvc remoteSvc) {
XAResourceArchive archive = this.remoteParticipantMap.get(remoteSvc);
return archive == null ? null : archive.getDescriptor();
}
public XAResourceDescriptor getRemoteCoordinator(String application) {
RemoteSvc remoteSvc = new RemoteSvc();
remoteSvc.setServiceKey(application);
XAResourceArchive archive = this.remoteParticipantMap.get(remoteSvc);
return archive == null ? null : archive.getDescriptor();
}
public TransactionXid getTransactionXid() {
throw new IllegalStateException();
}
public void setBeanFactory(TransactionBeanFactory tbf) {
this.beanFactory = tbf;
}
public boolean isLocalTransaction() {
return this.participantList.size() <= 1;
}
public Exception getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Exception createdAt) {
this.createdAt = createdAt;
}
public void setTransactionStrategy(TransactionStrategy transactionStrategy) {
this.transactionStrategy = transactionStrategy;
}
public TransactionExtra getTransactionalExtra() {
return transactionalExtra;
}
public void setTransactionalExtra(TransactionExtra transactionalExtra) {
this.transactionalExtra = transactionalExtra;
}
public TransactionContext getTransactionContext() {
return transactionContext;
}
public boolean isTiming() {
return timing;
}
public void setTiming(boolean timing) {
this.timing = timing;
}
public int getTransactionStatus() {
return transactionStatus;
}
public void setTransactionStatus(int transactionStatus) {
this.transactionStatus = transactionStatus;
}
public int getTransactionTimeout() {
return transactionTimeout;
}
public void setTransactionTimeout(int transactionTimeout) {
this.transactionTimeout = transactionTimeout;
}
public XAResourceArchive getParticipant() {
return participant;
}
public Map getRemoteParticipantMap() {
return remoteParticipantMap;
}
public Map getNativeParticipantMap() {
return nativeParticipantMap;
}
public List getParticipantList() {
return participantList;
}
public void setParticipant(XAResourceArchive participant) {
this.participant = participant;
}
public List getNativeParticipantList() {
return nativeParticipantList;
}
public List getRemoteParticipantList() {
return remoteParticipantList;
}
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/bytejta/TransactionManagerImpl.java
================================================
/**
* Copyright 2014-2016 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.bytejta;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.InvalidTransactionException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.Status;
import javax.transaction.SystemException;
import javax.transaction.xa.Xid;
import org.bytesoft.common.utils.ByteUtils;
import org.bytesoft.transaction.RollbackRequiredException;
import org.bytesoft.transaction.Transaction;
import org.bytesoft.transaction.TransactionBeanFactory;
import org.bytesoft.transaction.TransactionContext;
import org.bytesoft.transaction.TransactionManager;
import org.bytesoft.transaction.TransactionRepository;
import org.bytesoft.transaction.aware.TransactionBeanFactoryAware;
import org.bytesoft.transaction.aware.TransactionDebuggable;
import org.bytesoft.transaction.remote.RemoteCoordinator;
import org.bytesoft.transaction.supports.TransactionTimer;
import org.bytesoft.transaction.xa.TransactionXid;
import org.bytesoft.transaction.xa.XidFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TransactionManagerImpl
implements TransactionManager, TransactionTimer, TransactionBeanFactoryAware, TransactionDebuggable {
static final Logger logger = LoggerFactory.getLogger(TransactionManagerImpl.class);
@javax.inject.Inject
private TransactionBeanFactory beanFactory;
private int timeoutSeconds = 5 * 60;
private final Map thread2txMap = new ConcurrentHashMap();
private final Map xid2txMap = new ConcurrentHashMap();
private boolean debuggingEnabled;
public void begin() throws NotSupportedException, SystemException {
if (this.getTransaction() != null) {
throw new NotSupportedException();
}
XidFactory xidFactory = this.beanFactory.getXidFactory();
RemoteCoordinator transactionCoordinator = (RemoteCoordinator) this.beanFactory.getNativeParticipant();
int timeoutSeconds = this.timeoutSeconds;
TransactionContext transactionContext = new TransactionContext();
transactionContext.setPropagatedBy(transactionCoordinator.getIdentifier());
transactionContext.setCoordinator(true);
long createdTime = System.currentTimeMillis();
long expiredTime = createdTime + (timeoutSeconds * 1000L);
transactionContext.setCreatedTime(createdTime);
transactionContext.setExpiredTime(expiredTime);
TransactionXid globalXid = xidFactory.createGlobalXid();
transactionContext.setXid(globalXid);
TransactionImpl transaction = new TransactionImpl(transactionContext);
transaction.setBeanFactory(this.beanFactory);
transaction.setTransactionTimeout(this.timeoutSeconds);
if (this.debuggingEnabled) {
transaction.setCreatedAt(new Exception());
} // end-if (this.debuggingEnabled)
this.associateThread(transaction);
TransactionRepository transactionRepository = this.beanFactory.getTransactionRepository();
transactionRepository.putTransaction(globalXid, transaction);
// this.transactionStatistic.fireBeginTransaction(transaction);
logger.info("{}> begin-transaction", ByteUtils.byteArrayToString(globalXid.getGlobalTransactionId()));
}
public void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException,
IllegalStateException, SystemException {
Transaction transaction = this.getTransactionQuietly(); // this.desociateThread();
if (transaction == null) {
throw new IllegalStateException();
} else if (transaction.getTransactionStatus() == Status.STATUS_ROLLEDBACK) {
this.desociateThread();
throw new RollbackException();
} else if (transaction.getTransactionStatus() == Status.STATUS_COMMITTED) {
this.desociateThread();
return;
} else if (transaction.getTransactionStatus() == Status.STATUS_MARKED_ROLLBACK) {
this.rollback(transaction);
throw new HeuristicRollbackException();
} else if (transaction.getTransactionStatus() != Status.STATUS_ACTIVE) {
throw new IllegalStateException();
}
TransactionRepository transactionRepository = this.beanFactory.getTransactionRepository();
TransactionContext transactionContext = transaction.getTransactionContext();
TransactionXid transactionXid = transactionContext.getXid();
boolean beforeCompletionFailure = true;
try {
transaction.fireBeforeTransactionCompletion();
this.desociateThread();
this.stopTiming(transaction); // stop timing
beforeCompletionFailure = false;
} catch (RollbackRequiredException rrex) {
this.desociateThread();
transaction.rollback();
HeuristicRollbackException hrex = new HeuristicRollbackException();
hrex.initCause(rrex);
throw hrex;
} catch (SystemException ex) {
this.desociateThread();
transaction.rollback();
HeuristicRollbackException hrex = new HeuristicRollbackException();
hrex.initCause(ex);
throw hrex;
} catch (RuntimeException rex) {
this.desociateThread();
transaction.rollback();
HeuristicRollbackException hrex = new HeuristicRollbackException();
hrex.initCause(rex);
throw hrex;
} finally {
if (beforeCompletionFailure) {
transaction.fireAfterTransactionCompletion();
} // end-if (beforeCompletionFailure)
}
try {
transaction.commit();
transaction.forgetQuietly(); // forget transaction
} catch (IllegalStateException ex) {
logger.error("Error occurred while committing transaction.", ex);
transactionRepository.putErrorTransaction(transactionXid, transaction);
throw ex;
} catch (SecurityException ex) {
logger.error("Error occurred while committing transaction.", ex);
transactionRepository.putErrorTransaction(transactionXid, transaction);
throw ex;
} catch (RollbackException rex) {
logger.error("Error occurred while committing transaction.", rex);
transaction.forgetQuietly(); // forget transaction
throw rex;
} catch (HeuristicMixedException hmex) {
logger.error("Error occurred while committing transaction.", hmex);
transaction.forgetQuietly(); // forget transaction
throw hmex;
} catch (HeuristicRollbackException hrex) {
logger.error("Error occurred while committing transaction.", hrex);
transaction.forgetQuietly(); // forget transaction
throw hrex;
} catch (SystemException ex) {
logger.error("Error occurred while committing transaction.", ex);
transactionRepository.putErrorTransaction(transactionXid, transaction);
throw ex;
} catch (RuntimeException rex) {
logger.error("Error occurred while committing transaction.", rex);
transactionRepository.putErrorTransaction(transactionXid, transaction);
throw rex;
} finally {
transaction.fireAfterTransactionCompletion();
}
}
public void rollback() throws IllegalStateException, SecurityException, SystemException {
Transaction transaction = this.getTransactionQuietly(); // this.desociateThread();
if (transaction == null) {
throw new IllegalStateException();
} else if (transaction.getTransactionStatus() == Status.STATUS_ROLLEDBACK) {
this.desociateThread();
return;
} else if (transaction.getTransactionStatus() == Status.STATUS_COMMITTED) {
this.desociateThread();
throw new SystemException();
}
this.rollback(transaction);
}
protected void rollback(Transaction transaction) throws IllegalStateException, SecurityException, SystemException {
TransactionRepository transactionRepository = this.beanFactory.getTransactionRepository();
TransactionContext transactionContext = transaction.getTransactionContext();
TransactionXid transactionXid = transactionContext.getXid();
try {
transaction.fireBeforeTransactionCompletionQuietly();
this.desociateThread();
this.stopTiming(transaction); // stop timing
transaction.rollback();
transaction.forgetQuietly();
} catch (IllegalStateException ex) {
logger.error("Error occurred while rolling back transaction.", ex);
transactionRepository.putErrorTransaction(transactionXid, transaction);
throw ex;
} catch (SecurityException ex) {
logger.error("Error occurred while rolling back transaction.", ex);
transactionRepository.putErrorTransaction(transactionXid, transaction);
throw ex;
} catch (SystemException ex) {
logger.error("Error occurred while rolling back transaction.", ex);
transactionRepository.putErrorTransaction(transactionXid, transaction);
throw ex;
} catch (RuntimeException ex) {
logger.error("Error occurred while rolling back transaction.", ex);
transactionRepository.putErrorTransaction(transactionXid, transaction);
throw ex;
} finally {
transaction.fireAfterTransactionCompletion();
}
}
public void associateThread(Transaction transaction) {
TransactionContext transactionContext = transaction.getTransactionContext();
TransactionXid transactionXid = transactionContext.getXid();
this.xid2txMap.put(transactionXid, transaction);
this.thread2txMap.put(Thread.currentThread(), transaction);
}
public Transaction desociateThread() {
Transaction transaction = this.thread2txMap.remove(Thread.currentThread());
if (transaction == null) {
return null;
}
TransactionContext transactionContext = transaction.getTransactionContext();
this.xid2txMap.remove(transactionContext.getXid());
return transaction;
}
public Transaction suspend() throws RollbackRequiredException, SystemException {
Transaction transaction = this.desociateThread();
if (transaction == null) {
return null;
}
transaction.suspend();
return transaction;
}
public void resume(javax.transaction.Transaction tobj)
throws InvalidTransactionException, IllegalStateException, RollbackRequiredException, SystemException {
if (tobj == null) {
throw new InvalidTransactionException();
}
if (TransactionImpl.class.isInstance(tobj) == false) {
throw new InvalidTransactionException();
} else if (this.getTransaction() != null) {
throw new IllegalStateException();
}
TransactionImpl transaction = (TransactionImpl) tobj;
transaction.resume();
this.associateThread(transaction);
}
public int getStatus() throws SystemException {
Transaction transaction = this.getTransaction();
return transaction == null ? Status.STATUS_NO_TRANSACTION : transaction.getTransactionStatus();
}
public Transaction getTransaction(Xid transactionXid) {
return this.xid2txMap.get(transactionXid);
}
public Transaction getTransaction(Thread thread) {
return this.thread2txMap.get(thread);
}
public Transaction getTransactionQuietly() {
try {
return this.getTransaction();
} catch (SystemException ex) {
return null;
} catch (RuntimeException ex) {
return null;
}
}
public Transaction getTransaction() throws SystemException {
return this.thread2txMap.get(Thread.currentThread());
}
public void setRollbackOnlyQuietly() {
Transaction transaction = this.getTransactionQuietly();
if (transaction != null) {
transaction.setRollbackOnlyQuietly();
}
}
public void setRollbackOnly() throws IllegalStateException, SystemException {
Transaction transaction = this.getTransaction();
if (transaction == null) {
throw new SystemException();
}
transaction.setRollbackOnly();
}
public void setTransactionTimeout(int seconds) throws SystemException {
Transaction transaction = this.getTransaction();
if (transaction == null) {
throw new SystemException();
} else if (seconds < 0) {
throw new SystemException();
} else if (seconds == 0) {
// ignore
} else {
((TransactionImpl) transaction).changeTransactionTimeout(seconds * 1000);
}
}
public void timingExecution() {
List expiredTransactions = new ArrayList();
List activeTransactions = new ArrayList(this.thread2txMap.values());
long current = System.currentTimeMillis();
Iterator activeItr = activeTransactions.iterator();
while (activeItr.hasNext()) {
Transaction transaction = activeItr.next();
if (transaction.isTiming()) {
TransactionContext transactionContext = transaction.getTransactionContext();
if (transactionContext.getExpiredTime() <= current) {
expiredTransactions.add(transaction);
}
} // end-if (transaction.isTiming())
}
Iterator expiredItr = expiredTransactions.iterator();
while (activeItr.hasNext()) {
Transaction transaction = expiredItr.next();
if (transaction.getTransactionStatus() == Status.STATUS_ACTIVE
|| transaction.getTransactionStatus() == Status.STATUS_MARKED_ROLLBACK) {
this.timingRollback(transaction);
}
}
}
private void timingRollback(Transaction transaction) {
TransactionContext transactionContext = transaction.getTransactionContext();
TransactionXid globalXid = transactionContext.getXid();
TransactionRepository transactionRepository = this.beanFactory.getTransactionRepository();
try {
this.associateThread(transaction);
transaction.fireBeforeTransactionCompletionQuietly();
this.desociateThread();
transaction.rollback();
transaction.forgetQuietly(); // forget transaction
} catch (Exception ex) {
transactionRepository.putErrorTransaction(globalXid, transaction);
} finally {
transaction.fireAfterTransactionCompletion();
}
}
public void stopTiming(Transaction transaction) {
if (TransactionImpl.class.isInstance(transaction)) {
((TransactionImpl) transaction).stopTiming();
}
}
public boolean isDebuggingEnabled() {
return debuggingEnabled;
}
public void setDebuggingEnabled(boolean debuggingEnabled) {
this.debuggingEnabled = debuggingEnabled;
}
public int getTimeoutSeconds() {
return timeoutSeconds;
}
public void setTimeoutSeconds(int timeoutSeconds) {
this.timeoutSeconds = timeoutSeconds;
}
public TransactionBeanFactory getBeanFactory() {
return this.beanFactory;
}
public void setBeanFactory(TransactionBeanFactory tbf) {
this.beanFactory = tbf;
}
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/bytejta/TransactionRecoveryImpl.java
================================================
/**
* Copyright 2014-2016 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.bytejta;
import java.util.List;
import java.util.Map;
import javax.transaction.Status;
import javax.transaction.SystemException;
import javax.transaction.xa.XAResource;
import org.apache.commons.lang3.StringUtils;
import org.bytesoft.bytejta.supports.resource.RemoteResourceDescriptor;
import org.bytesoft.common.utils.ByteUtils;
import org.bytesoft.common.utils.CommonUtils;
import org.bytesoft.transaction.CommitRequiredException;
import org.bytesoft.transaction.RollbackRequiredException;
import org.bytesoft.transaction.Transaction;
import org.bytesoft.transaction.TransactionBeanFactory;
import org.bytesoft.transaction.TransactionContext;
import org.bytesoft.transaction.TransactionRecovery;
import org.bytesoft.transaction.TransactionRepository;
import org.bytesoft.transaction.archive.TransactionArchive;
import org.bytesoft.transaction.archive.XAResourceArchive;
import org.bytesoft.transaction.aware.TransactionBeanFactoryAware;
import org.bytesoft.transaction.logging.TransactionLogger;
import org.bytesoft.transaction.recovery.TransactionRecoveryCallback;
import org.bytesoft.transaction.recovery.TransactionRecoveryListener;
import org.bytesoft.transaction.remote.RemoteSvc;
import org.bytesoft.transaction.supports.resource.XAResourceDescriptor;
import org.bytesoft.transaction.xa.TransactionXid;
import org.bytesoft.transaction.xa.XidFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TransactionRecoveryImpl implements TransactionRecovery, TransactionBeanFactoryAware {
static final Logger logger = LoggerFactory.getLogger(TransactionRecoveryImpl.class);
static final long SECOND_MILLIS = 1000L;
private TransactionRecoveryListener listener;
@javax.inject.Inject
private TransactionBeanFactory beanFactory;
private volatile boolean initialized;
public synchronized void timingRecover() {
TransactionRepository transactionRepository = beanFactory.getTransactionRepository();
List transactions = transactionRepository.getErrorTransactionList();
int total = transactions == null ? 0 : transactions.size(), value = 0;
for (int i = 0; transactions != null && i < transactions.size(); i++) {
Transaction transaction = transactions.get(i);
TransactionContext transactionContext = transaction.getTransactionContext();
TransactionXid xid = transactionContext.getXid();
int recoveredTimes = transactionContext.getRecoveredTimes() > 10 ? 10 : transactionContext.getRecoveredTimes();
long recoverMillis = transactionContext.getCreatedTime() + SECOND_MILLIS * 60L * (long) Math.pow(2, recoveredTimes);
if (System.currentTimeMillis() < recoverMillis) {
continue;
} // end-if (System.currentTimeMillis() < recoverMillis)
try {
this.recoverTransaction(transaction);
value++;
} catch (CommitRequiredException ex) {
logger.debug("{}> recover: branch={}, message= commit-required",
ByteUtils.byteArrayToString(xid.getGlobalTransactionId()),
ByteUtils.byteArrayToString(xid.getBranchQualifier()), ex);
continue;
} catch (RollbackRequiredException ex) {
logger.debug("{}> recover: branch={}, message= rollback-required",
ByteUtils.byteArrayToString(xid.getGlobalTransactionId()),
ByteUtils.byteArrayToString(xid.getBranchQualifier()), ex);
continue;
} catch (SystemException ex) {
logger.debug("{}> recover: branch={}, message= {}", ByteUtils.byteArrayToString(xid.getGlobalTransactionId()),
ByteUtils.byteArrayToString(xid.getBranchQualifier()), ex.getMessage(), ex);
continue;
} catch (RuntimeException ex) {
logger.debug("{}> recover: branch={}, message= {}", ByteUtils.byteArrayToString(xid.getGlobalTransactionId()),
ByteUtils.byteArrayToString(xid.getBranchQualifier()), ex.getMessage(), ex);
continue;
}
}
logger.debug("[transaction-recovery] total= {}, success= {}", total, value);
}
public void recoverTransaction(Transaction transaction)
throws CommitRequiredException, RollbackRequiredException, SystemException {
TransactionContext transactionContext = transaction.getTransactionContext();
boolean coordinator = transactionContext.isCoordinator();
if (coordinator) {
transaction.recover();
this.recoverCoordinator(transaction);
} else {
transaction.recover();
this.recoverParticipant(transaction);
}
}
protected void recoverCoordinator(Transaction transaction)
throws CommitRequiredException, RollbackRequiredException, SystemException {
switch (transaction.getTransactionStatus()) {
case Status.STATUS_ACTIVE:
case Status.STATUS_MARKED_ROLLBACK:
case Status.STATUS_PREPARING:
case Status.STATUS_ROLLING_BACK:
case Status.STATUS_UNKNOWN:
transaction.recoveryRollback();
transaction.forgetQuietly();
break;
case Status.STATUS_PREPARED:
case Status.STATUS_COMMITTING:
transaction.recoveryCommit();
transaction.forgetQuietly();
break;
case Status.STATUS_COMMITTED:
case Status.STATUS_ROLLEDBACK:
transaction.forgetQuietly();
break;
default:
logger.debug("Current transaction has already been completed.");
}
}
protected void recoverParticipant(Transaction transaction)
throws CommitRequiredException, RollbackRequiredException, SystemException {
TransactionImpl transactionImpl = (TransactionImpl) transaction;
switch (transaction.getTransactionStatus()) {
case Status.STATUS_PREPARED:
case Status.STATUS_COMMITTING:
break;
case Status.STATUS_COMMITTED:
case Status.STATUS_ROLLEDBACK:
break;
case Status.STATUS_ACTIVE:
case Status.STATUS_MARKED_ROLLBACK:
case Status.STATUS_PREPARING:
case Status.STATUS_UNKNOWN:
case Status.STATUS_ROLLING_BACK:
default:
transactionImpl.recoveryRollback();
transactionImpl.forgetQuietly();
}
}
public synchronized void startRecovery() {
final TransactionRepository transactionRepository = beanFactory.getTransactionRepository();
final TransactionLogger transactionLogger = beanFactory.getTransactionLogger();
transactionLogger.recover(new TransactionRecoveryCallback() {
public void recover(TransactionArchive archive) {
try {
TransactionImpl transaction = (TransactionImpl) reconstruct(archive);
if (listener != null) {
listener.onRecovery(transaction);
}
TransactionContext transactionContext = transaction.getTransactionContext();
TransactionXid globalXid = transactionContext.getXid();
transactionRepository.putTransaction(globalXid, transaction);
transactionRepository.putErrorTransaction(globalXid, transaction);
} catch (IllegalStateException ex) {
transactionLogger.deleteTransaction(archive);
}
}
});
TransactionCoordinator transactionCoordinator = //
(TransactionCoordinator) this.beanFactory.getNativeParticipant();
transactionCoordinator.markParticipantReady();
this.initialized = true; // timingRecovery should be executed after initialization
}
public org.bytesoft.transaction.Transaction reconstruct(TransactionArchive archive) throws IllegalStateException {
XidFactory xidFactory = this.beanFactory.getXidFactory();
TransactionContext transactionContext = new TransactionContext();
TransactionXid xid = (TransactionXid) archive.getXid();
transactionContext.setXid(xidFactory.createGlobalXid(xid.getGlobalTransactionId()));
transactionContext.setRecoveried(true);
transactionContext.setCoordinator(archive.isCoordinator());
transactionContext.setPropagatedBy(archive.getPropagatedBy());
transactionContext.setRecoveredTimes(archive.getRecoveredTimes());
transactionContext.setCreatedTime(archive.getRecoveredAt());
TransactionImpl transaction = new TransactionImpl(transactionContext);
transaction.setBeanFactory(this.beanFactory);
transaction.setTransactionStatus(archive.getStatus());
List nativeResources = archive.getNativeResources();
transaction.getNativeParticipantList().addAll(nativeResources);
transaction.setParticipant(archive.getOptimizedResource());
List remoteResources = archive.getRemoteResources();
transaction.getRemoteParticipantList().addAll(remoteResources);
List participants = transaction.getParticipantList();
Map nativeParticipantMap = transaction.getNativeParticipantMap();
Map remoteParticipantMap = transaction.getRemoteParticipantMap();
if (archive.getOptimizedResource() != null) {
XAResourceArchive optimized = archive.getOptimizedResource();
XAResourceDescriptor descriptor = optimized.getDescriptor();
String identifier = descriptor.getIdentifier();
if (RemoteResourceDescriptor.class.isInstance(descriptor)) {
RemoteSvc remoteSvc = CommonUtils.getRemoteSvc(identifier);
remoteParticipantMap.put(remoteSvc, optimized);
} else {
nativeParticipantMap.put(identifier, optimized);
}
participants.add(optimized);
}
for (int i = 0; i < nativeResources.size(); i++) {
XAResourceArchive element = nativeResources.get(i);
XAResourceDescriptor descriptor = element.getDescriptor();
String identifier = StringUtils.trimToEmpty(descriptor.getIdentifier());
nativeParticipantMap.put(identifier, element);
}
participants.addAll(nativeResources);
for (int i = 0; i < remoteResources.size(); i++) {
XAResourceArchive element = remoteResources.get(i);
XAResourceDescriptor descriptor = element.getDescriptor();
String identifier = StringUtils.trimToEmpty(descriptor.getIdentifier());
if (RemoteResourceDescriptor.class.isInstance(descriptor)) {
RemoteSvc remoteSvc = CommonUtils.getRemoteSvc(identifier);
remoteParticipantMap.put(remoteSvc, element);
} // end-if (RemoteResourceDescriptor.class.isInstance(descriptor))
}
participants.addAll(remoteResources);
transaction.recoverTransactionStrategy(archive.getTransactionStrategyType());
if (archive.getVote() == XAResource.XA_RDONLY) {
throw new IllegalStateException("Transaction has already been completed!");
}
return transaction;
}
public synchronized void branchRecover() {
// For a completed global transaction, if its branch receives a business request again, it will be rolled back by the
// RM's timeout mechanism, there is no need to deal with it.
}
public boolean isInitialized() {
return initialized;
}
public TransactionBeanFactory getBeanFactory() {
return beanFactory;
}
public void setBeanFactory(TransactionBeanFactory tbf) {
this.beanFactory = tbf;
}
public TransactionRecoveryListener getListener() {
return listener;
}
public void setListener(TransactionRecoveryListener listener) {
this.listener = listener;
}
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/bytejta/TransactionRepositoryImpl.java
================================================
/**
* Copyright 2014-2016 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.bytejta;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.bytesoft.transaction.Transaction;
import org.bytesoft.transaction.TransactionRepository;
import org.bytesoft.transaction.xa.TransactionXid;
public class TransactionRepositoryImpl implements TransactionRepository {
private final Map xidToTxMap = new ConcurrentHashMap();
private final Map xidToErrTxMap = new ConcurrentHashMap();
public void putTransaction(TransactionXid globalXid, Transaction transaction) {
this.xidToTxMap.put(globalXid, transaction);
}
public Transaction getTransaction(TransactionXid globalXid) {
return this.xidToTxMap.get(globalXid);
}
public Transaction removeTransaction(TransactionXid globalXid) {
return this.xidToTxMap.remove(globalXid);
}
public void putErrorTransaction(TransactionXid globalXid, Transaction transaction) {
this.xidToErrTxMap.put(globalXid, transaction);
}
public Transaction getErrorTransaction(TransactionXid globalXid) {
return this.xidToErrTxMap.get(globalXid);
}
public Transaction removeErrorTransaction(TransactionXid globalXid) {
return this.xidToErrTxMap.remove(globalXid);
}
public List getErrorTransactionList() {
return new ArrayList(this.xidToErrTxMap.values());
}
public List getActiveTransactionList() {
return new ArrayList(this.xidToTxMap.values());
}
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/bytejta/TransactionStrategy.java
================================================
/**
* Copyright 2014-2017 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.bytejta;
import javax.transaction.HeuristicCommitException;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.SystemException;
import javax.transaction.xa.Xid;
import org.bytesoft.transaction.CommitRequiredException;
import org.bytesoft.transaction.RollbackRequiredException;
public interface TransactionStrategy /* extends TransactionBeanFactoryAware */ {
public int TRANSACTION_STRATEGY_VACANT = 0;
public int TRANSACTION_STRATEGY_SIMPLE = 1;
public int TRANSACTION_STRATEGY_COMMON = 2;
public int TRANSACTION_STRATEGY_LRO = 3;
public int prepare(Xid xid) throws RollbackRequiredException, CommitRequiredException;
public void commit(Xid xid, boolean onePhaseCommit)
throws HeuristicMixedException, HeuristicRollbackException, IllegalStateException, SystemException;
public void rollback(Xid xid)
throws HeuristicMixedException, HeuristicCommitException, IllegalStateException, SystemException;
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/bytejta/UserTransactionImpl.java
================================================
/**
* Copyright 2014-2016 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.bytejta;
import java.io.Serializable;
import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.SystemException;
import javax.transaction.TransactionManager;
import javax.transaction.UserTransaction;
public class UserTransactionImpl implements UserTransaction, Referenceable, Serializable {
private static final long serialVersionUID = 1L;
@javax.inject.Inject
private transient TransactionManager transactionManager;
public void begin() throws NotSupportedException, SystemException {
this.transactionManager.begin();
}
public void commit() throws HeuristicMixedException, HeuristicRollbackException, IllegalStateException,
RollbackException, SecurityException, SystemException {
this.transactionManager.commit();
}
public int getStatus() throws SystemException {
return this.transactionManager.getStatus();
}
public void rollback() throws IllegalStateException, SecurityException, SystemException {
this.transactionManager.rollback();
}
public void setRollbackOnly() throws IllegalStateException, SystemException {
this.transactionManager.setRollbackOnly();
}
public void setTransactionTimeout(int timeout) throws SystemException {
this.transactionManager.setTransactionTimeout(timeout);
}
public Reference getReference() throws NamingException {
throw new NamingException("Not supported yet!");
}
public TransactionManager getTransactionManager() {
return transactionManager;
}
public void setTransactionManager(TransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/bytejta/VacantTransactionLock.java
================================================
/**
* Copyright 2014-2017 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.bytejta;
import org.bytesoft.transaction.TransactionLock;
import org.bytesoft.transaction.xa.TransactionXid;
public class VacantTransactionLock implements TransactionLock {
public boolean lockTransaction(TransactionXid transactionXid, String identifier) {
return true;
}
public void unlockTransaction(TransactionXid transactionXid, String identifier) {
}
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/bytejta/logging/ArchiveDeserializerImpl.java
================================================
/**
* Copyright 2014-2016 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.bytejta.logging;
import org.bytesoft.transaction.archive.TransactionArchive;
import org.bytesoft.transaction.archive.XAResourceArchive;
import org.bytesoft.transaction.logging.ArchiveDeserializer;
import org.bytesoft.transaction.xa.TransactionXid;
public class ArchiveDeserializerImpl implements ArchiveDeserializer {
static final byte TYPE_TRANSACTION = 0x0;
static final byte TYPE_XA_RESOURCE = 0x1;
private ArchiveDeserializer xaResourceArchiveDeserializer;
private ArchiveDeserializer transactionArchiveDeserializer;
public byte[] serialize(TransactionXid xid, Object archive) {
if (TransactionArchive.class.isInstance(archive)) {
byte[] array = this.transactionArchiveDeserializer.serialize(xid, archive);
byte[] byteArray = new byte[array.length + 1];
byteArray[0] = TYPE_TRANSACTION;
System.arraycopy(array, 0, byteArray, 1, array.length);
return byteArray;
} else if (XAResourceArchive.class.isInstance(archive)) {
byte[] array = this.xaResourceArchiveDeserializer.serialize(xid, archive);
byte[] byteArray = new byte[array.length + 1];
byteArray[0] = TYPE_XA_RESOURCE;
System.arraycopy(array, 0, byteArray, 1, array.length);
return byteArray;
} else {
throw new IllegalArgumentException();
}
}
public Object deserialize(TransactionXid xid, byte[] array) {
if (array == null || array.length <= 1) {
throw new IllegalArgumentException();
}
byte type = array[0];
if (type == TYPE_TRANSACTION) {
byte[] byteArray = new byte[array.length - 1];
System.arraycopy(array, 1, byteArray, 0, byteArray.length);
return this.transactionArchiveDeserializer.deserialize(xid, byteArray);
} else if (type == TYPE_XA_RESOURCE) {
byte[] byteArray = new byte[array.length - 1];
System.arraycopy(array, 1, byteArray, 0, byteArray.length);
return this.xaResourceArchiveDeserializer.deserialize(xid, byteArray);
} else {
throw new IllegalArgumentException();
}
}
public ArchiveDeserializer getXaResourceArchiveDeserializer() {
return xaResourceArchiveDeserializer;
}
public void setXaResourceArchiveDeserializer(ArchiveDeserializer xaResourceArchiveDeserializer) {
this.xaResourceArchiveDeserializer = xaResourceArchiveDeserializer;
}
public ArchiveDeserializer getTransactionArchiveDeserializer() {
return transactionArchiveDeserializer;
}
public void setTransactionArchiveDeserializer(ArchiveDeserializer transactionArchiveDeserializer) {
this.transactionArchiveDeserializer = transactionArchiveDeserializer;
}
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/bytejta/logging/SampleTransactionLogger.java
================================================
/**
* Copyright 2014-2016 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.bytejta.logging;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.transaction.xa.Xid;
import org.apache.commons.lang3.StringUtils;
import org.bytesoft.bytejta.logging.store.VirtualLoggingSystemImpl;
import org.bytesoft.common.utils.ByteUtils;
import org.bytesoft.transaction.TransactionBeanFactory;
import org.bytesoft.transaction.archive.TransactionArchive;
import org.bytesoft.transaction.archive.XAResourceArchive;
import org.bytesoft.transaction.aware.TransactionBeanFactoryAware;
import org.bytesoft.transaction.aware.TransactionEndpointAware;
import org.bytesoft.transaction.logging.ArchiveDeserializer;
import org.bytesoft.transaction.logging.LoggingFlushable;
import org.bytesoft.transaction.logging.TransactionLogger;
import org.bytesoft.transaction.logging.store.VirtualLoggingListener;
import org.bytesoft.transaction.logging.store.VirtualLoggingRecord;
import org.bytesoft.transaction.logging.store.VirtualLoggingSystem;
import org.bytesoft.transaction.recovery.TransactionRecoveryCallback;
import org.bytesoft.transaction.xa.TransactionXid;
import org.bytesoft.transaction.xa.XidFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SampleTransactionLogger extends VirtualLoggingSystemImpl
implements TransactionLogger, LoggingFlushable, TransactionBeanFactoryAware, TransactionEndpointAware {
static final Logger logger = LoggerFactory.getLogger(SampleTransactionLogger.class);
@javax.inject.Inject
private TransactionBeanFactory beanFactory;
private String identifier;
@PostConstruct
public void construct() throws IOException {
this.initializeIfNecessary();
}
private void initializeIfNecessary() throws IllegalStateException {
if (StringUtils.isNotBlank(this.identifier)) {
try {
super.construct();
} catch (IOException error) {
throw new IllegalStateException("Error occurred while initializing tx-log!", error);
}
} // end-if (StringUtils.isNotBlank(this.endpoint))
}
public void createTransaction(TransactionArchive archive) {
ArchiveDeserializer deserializer = this.beanFactory.getArchiveDeserializer();
try {
byte[] byteArray = deserializer.serialize((TransactionXid) archive.getXid(), archive);
this.create(archive.getXid(), byteArray);
} catch (RuntimeException rex) {
logger.error("Error occurred while creating transaction-archive.", rex);
}
}
public void updateTransaction(TransactionArchive archive) {
ArchiveDeserializer deserializer = this.beanFactory.getArchiveDeserializer();
try {
byte[] byteArray = deserializer.serialize((TransactionXid) archive.getXid(), archive);
this.modify(archive.getXid(), byteArray);
} catch (RuntimeException rex) {
logger.error("Error occurred while modifying transaction-archive.", rex);
}
}
public void deleteTransaction(TransactionArchive archive) {
try {
this.delete(archive.getXid());
} catch (RuntimeException rex) {
logger.error("Error occurred while deleting transaction-archive.", rex);
}
}
public void createParticipant(XAResourceArchive archive) {
}
public void updateParticipant(XAResourceArchive archive) {
ArchiveDeserializer deserializer = this.beanFactory.getArchiveDeserializer();
try {
byte[] byteArray = deserializer.serialize((TransactionXid) archive.getXid(), archive);
this.modify(archive.getXid(), byteArray);
} catch (RuntimeException rex) {
logger.error("Error occurred while modifying resource-archive.", rex);
}
}
public void deleteParticipant(XAResourceArchive archive) {
}
public void createResource(XAResourceArchive archive) {
}
public void updateResource(XAResourceArchive archive) {
}
public void deleteResource(XAResourceArchive archive) {
}
public List compressIfNecessary(List recordList) {
ArchiveDeserializer deserializer = this.beanFactory.getArchiveDeserializer();
XidFactory xidFactory = this.beanFactory.getXidFactory();
List resultList = new ArrayList();
Map xidMap = new HashMap();
for (int index = 0; recordList != null && index < recordList.size(); index++) {
VirtualLoggingRecord record = recordList.get(index);
byte[] byteArray = record.getContent();
byte[] keyByteArray = new byte[XidFactory.GLOBAL_TRANSACTION_LENGTH];
System.arraycopy(byteArray, 0, keyByteArray, 0, keyByteArray.length);
// int operator = byteArray[keyByteArray.length];
byte[] valueByteArray = new byte[byteArray.length - XidFactory.GLOBAL_TRANSACTION_LENGTH - 1 - 4];
System.arraycopy(byteArray, XidFactory.GLOBAL_TRANSACTION_LENGTH + 1 + 4, valueByteArray, 0, valueByteArray.length);
TransactionXid xid = xidFactory.createGlobalXid(keyByteArray);
Object obj = deserializer.deserialize(xid, valueByteArray);
if (TransactionArchive.class.isInstance(obj)) {
xidMap.put(xid, (TransactionArchive) obj);
} else if (XAResourceArchive.class.isInstance(obj)) {
TransactionArchive archive = xidMap.get(xid);
if (archive == null) {
logger.error("Error occurred while compressing resource archive: {}", obj);
continue;
}
XAResourceArchive resourceArchive = (XAResourceArchive) obj;
boolean matched = false;
List nativeResources = archive.getNativeResources();
for (int i = 0; matched == false && nativeResources != null && i < nativeResources.size(); i++) {
XAResourceArchive element = nativeResources.get(i);
if (resourceArchive.getXid().equals(element.getXid())) {
matched = true;
nativeResources.set(i, resourceArchive);
}
}
XAResourceArchive optimizedResource = archive.getOptimizedResource();
if (matched == false && optimizedResource != null) {
if (resourceArchive.getXid().equals(optimizedResource.getXid())) {
matched = true;
archive.setOptimizedResource(resourceArchive);
}
}
List remoteResources = archive.getRemoteResources();
for (int i = 0; matched == false && remoteResources != null && i < remoteResources.size(); i++) {
XAResourceArchive element = remoteResources.get(i);
if (resourceArchive.getXid().equals(element.getXid())) {
matched = true;
remoteResources.set(i, resourceArchive);
}
}
if (matched == false) {
logger.error("Error occurred while compressing resource archive: {}, invalid resoure!", obj);
}
} else {
logger.error("unkown resource: {}!", obj);
}
}
for (Iterator> itr = xidMap.entrySet().iterator(); itr.hasNext();) {
Map.Entry entry = itr.next();
TransactionXid xid = entry.getKey();
TransactionArchive value = entry.getValue();
byte[] globalByteArray = xid.getGlobalTransactionId();
byte[] keyByteArray = new byte[XidFactory.GLOBAL_TRANSACTION_LENGTH];
byte[] valueByteArray = deserializer.serialize(xid, value);
byte[] sizeByteArray = ByteUtils.intToByteArray(valueByteArray.length);
System.arraycopy(globalByteArray, 0, keyByteArray, 0, XidFactory.GLOBAL_TRANSACTION_LENGTH);
byte[] byteArray = new byte[XidFactory.GLOBAL_TRANSACTION_LENGTH + 1 + 4 + valueByteArray.length];
System.arraycopy(keyByteArray, 0, byteArray, 0, keyByteArray.length);
byteArray[keyByteArray.length] = OPERATOR_CREATE;
System.arraycopy(sizeByteArray, 0, byteArray, XidFactory.GLOBAL_TRANSACTION_LENGTH + 1, sizeByteArray.length);
System.arraycopy(valueByteArray, 0, byteArray, XidFactory.GLOBAL_TRANSACTION_LENGTH + 1 + 4, valueByteArray.length);
VirtualLoggingRecord record = new VirtualLoggingRecord();
record.setIdentifier(xid);
record.setOperator(OPERATOR_CREATE);
record.setValue(valueByteArray);
record.setContent(byteArray);
resultList.add(record);
}
return resultList;
}
public void recover(TransactionRecoveryCallback callback) {
final Map xidMap = new HashMap();
final ArchiveDeserializer deserializer = this.beanFactory.getArchiveDeserializer();
final XidFactory xidFactory = this.beanFactory.getXidFactory();
this.traversal(new VirtualLoggingListener() {
public void recvOperation(VirtualLoggingRecord action) {
Xid xid = action.getIdentifier();
int operator = action.getOperator();
if (VirtualLoggingSystem.OPERATOR_DELETE == operator) {
xidMap.remove(xid);
} else if (xidMap.containsKey(xid) == false) {
xidMap.put(xid, null);
}
}
});
this.traversal(new VirtualLoggingListener() {
public void recvOperation(VirtualLoggingRecord action) {
Xid xid = action.getIdentifier();
if (xidMap.containsKey(xid)) {
this.execOperation(action);
}
}
public void execOperation(VirtualLoggingRecord action) {
Xid identifier = action.getIdentifier();
TransactionXid xid = xidFactory.createGlobalXid(identifier.getGlobalTransactionId());
Object obj = deserializer.deserialize(xid, action.getValue());
if (TransactionArchive.class.isInstance(obj)) {
TransactionArchive archive = (TransactionArchive) obj;
xidMap.put(identifier, archive);
} else if (XAResourceArchive.class.isInstance(obj)) {
TransactionArchive archive = xidMap.get(identifier);
if (archive == null) {
logger.error("Error occurred while recovering resource archive: {}", obj);
return;
}
XAResourceArchive resourceArchive = (XAResourceArchive) obj;
boolean matched = false;
List nativeResources = archive.getNativeResources();
for (int i = 0; matched == false && nativeResources != null && i < nativeResources.size(); i++) {
XAResourceArchive element = nativeResources.get(i);
if (resourceArchive.getXid().equals(element.getXid())) {
matched = true;
nativeResources.set(i, resourceArchive);
}
}
XAResourceArchive optimizedResource = archive.getOptimizedResource();
if (matched == false && optimizedResource != null) {
if (resourceArchive.getXid().equals(optimizedResource.getXid())) {
matched = true;
archive.setOptimizedResource(resourceArchive);
}
}
List remoteResources = archive.getRemoteResources();
for (int i = 0; matched == false && remoteResources != null && i < remoteResources.size(); i++) {
XAResourceArchive element = remoteResources.get(i);
if (resourceArchive.getXid().equals(element.getXid())) {
matched = true;
remoteResources.set(i, resourceArchive);
}
}
if (matched == false) {
logger.error("Error occurred while recovering resource archive: {}, invalid resoure!", obj);
}
}
}
});
for (Iterator> itr = xidMap.entrySet().iterator(); itr.hasNext();) {
Map.Entry entry = itr.next();
TransactionArchive archive = entry.getValue();
if (archive == null) {
continue;
} else {
try {
callback.recover(archive);
} catch (RuntimeException rex) {
logger.error("Error occurred while recovering transaction(xid= {}).", archive.getXid(), rex);
}
}
}
}
public File getDefaultDirectory() {
String address = StringUtils.trimToEmpty(this.identifier);
File directory = new File(String.format("bytejta/%s", address.replaceAll("\\W", "_")));
if (directory.exists() == false) {
try {
boolean created = directory.mkdirs();
if (created == false) {
logger.error("Failed to create directory {}!", directory.getAbsolutePath());
} // end-if (created == false)
} catch (SecurityException ex) {
logger.error("Error occurred while creating directory {}!", directory.getAbsolutePath(), ex);
}
}
return directory;
}
public int getMajorVersion() {
return 0;
}
public int getMinorVersion() {
return 6;
}
public String getLoggingFilePrefix() {
return "bytejta-";
}
public String getLoggingIdentifier() {
return "org.bytesoft.bytejta.logging.sample";
}
public String getEndpoint() {
return identifier;
}
public void setEndpoint(String identifier) {
this.identifier = identifier;
this.initializeIfNecessary();
}
public TransactionBeanFactory getBeanFactory() {
return beanFactory;
}
public void setBeanFactory(TransactionBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/bytejta/logging/deserializer/TransactionArchiveDeserializer.java
================================================
/**
* Copyright 2014-2016 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.bytejta.logging.deserializer;
import java.nio.ByteBuffer;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.bytesoft.common.utils.ByteUtils;
import org.bytesoft.common.utils.CommonUtils;
import org.bytesoft.transaction.archive.TransactionArchive;
import org.bytesoft.transaction.archive.XAResourceArchive;
import org.bytesoft.transaction.logging.ArchiveDeserializer;
import org.bytesoft.transaction.remote.RemoteNode;
import org.bytesoft.transaction.xa.TransactionXid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TransactionArchiveDeserializer implements ArchiveDeserializer {
static final Logger logger = LoggerFactory.getLogger(TransactionArchiveDeserializer.class);
private ArchiveDeserializer resourceArchiveDeserializer;
public byte[] serialize(TransactionXid xid, Object obj) {
TransactionArchive archive = (TransactionArchive) obj;
String propagatedBy = String.valueOf(archive.getPropagatedBy());
// String[] address = propagatedBy.split("\\s*\\:\\s*");
RemoteNode remoteNode = CommonUtils.getRemoteNode(propagatedBy);
byte[] hostByteArray = new byte[4];
byte[] nameByteArray = new byte[0];
byte[] portByteArray = new byte[2];
if (remoteNode != null) {
String hostStr = remoteNode.getServerHost();
String nameStr = remoteNode.getServiceKey();
String portStr = String.valueOf(remoteNode.getServerPort());
String[] hostArray = hostStr.split("\\s*\\.\\s*");
for (int i = 0; hostArray.length == 4 && i < hostArray.length; i++) {
try {
int value = Integer.valueOf(hostArray[i]);
hostByteArray[i] = (byte) (value - 128);
} catch (RuntimeException rex) {
logger.debug(rex.getMessage(), rex);
}
}
nameByteArray = StringUtils.isBlank(nameStr) ? new byte[0] : nameStr.getBytes();
try {
short port = (short) (Integer.valueOf(portStr) - 32768);
byte[] byteArray = ByteUtils.shortToByteArray(port);
System.arraycopy(byteArray, 0, portByteArray, 0, 2);
} catch (RuntimeException rex) {
logger.debug(rex.getMessage(), rex);
}
}
long recoveredMillis = archive.getRecoveredAt();
int recoveredTimes = archive.getRecoveredTimes();
XAResourceArchive optimizedArchive = archive.getOptimizedResource();
List nativeArchiveList = archive.getNativeResources();
List remoteArchiveList = archive.getRemoteResources();
int optimizedArchiveNumber = optimizedArchive == null ? 0 : 1;
int nativeArchiveNumber = nativeArchiveList.size();
int remoteArchiveNumber = remoteArchiveList.size();
int transactionStrategy = archive.getTransactionStrategyType();
int length = 3 + 3 + 1 + 4 + 1 + nameByteArray.length + 2 + 8 + 1;
byte[][] nativeByteArray = new byte[nativeArchiveNumber][];
for (int i = 0; i < nativeArchiveNumber; i++) {
XAResourceArchive resourceArchive = nativeArchiveList.get(i);
byte[] resourceByteArray = this.resourceArchiveDeserializer.serialize(xid, resourceArchive);
byte[] lengthByteArray = ByteUtils.shortToByteArray((short) resourceByteArray.length);
byte[] elementByteArray = new byte[resourceByteArray.length + 2];
System.arraycopy(lengthByteArray, 0, elementByteArray, 0, lengthByteArray.length);
System.arraycopy(resourceByteArray, 0, elementByteArray, 2, resourceByteArray.length);
nativeByteArray[i] = elementByteArray;
length = length + elementByteArray.length;
}
byte[] optimizedByteArray = new byte[0];
if (optimizedArchiveNumber > 0) {
byte[] resourceByteArray = this.resourceArchiveDeserializer.serialize(xid, optimizedArchive);
byte[] lengthByteArray = ByteUtils.shortToByteArray((short) resourceByteArray.length);
byte[] elementByteArray = new byte[resourceByteArray.length + 2];
System.arraycopy(lengthByteArray, 0, elementByteArray, 0, lengthByteArray.length);
System.arraycopy(resourceByteArray, 0, elementByteArray, 2, resourceByteArray.length);
optimizedByteArray = elementByteArray;
length = length + elementByteArray.length;
}
byte[][] remoteByteArray = new byte[remoteArchiveNumber][];
for (int i = 0; i < remoteArchiveNumber; i++) {
XAResourceArchive resourceArchive = remoteArchiveList.get(i);
byte[] resourceByteArray = this.resourceArchiveDeserializer.serialize(xid, resourceArchive);
byte[] lengthByteArray = ByteUtils.shortToByteArray((short) resourceByteArray.length);
byte[] elementByteArray = new byte[resourceByteArray.length + 2];
System.arraycopy(lengthByteArray, 0, elementByteArray, 0, lengthByteArray.length);
System.arraycopy(resourceByteArray, 0, elementByteArray, 2, resourceByteArray.length);
remoteByteArray[i] = elementByteArray;
length = length + elementByteArray.length;
}
int position = 0;
byte[] byteArray = new byte[length];
byteArray[position++] = (byte) archive.getStatus();
byteArray[position++] = (byte) archive.getVote();
byteArray[position++] = archive.isCoordinator() ? (byte) 0x1 : (byte) 0x0;
byteArray[position++] = (byte) nativeArchiveNumber;
byteArray[position++] = (byte) optimizedArchiveNumber;
byteArray[position++] = (byte) remoteArchiveNumber;
byteArray[position++] = (byte) transactionStrategy;
System.arraycopy(hostByteArray, 0, byteArray, position, 4);
position = position + 4;
byteArray[position++] = (byte) (nameByteArray.length - 128);
System.arraycopy(nameByteArray, 0, byteArray, position, nameByteArray.length);
position = position + nameByteArray.length;
System.arraycopy(portByteArray, 0, byteArray, position, 2);
position = position + 2;
byteArray[position++] = (byte) (recoveredTimes - 128);
byte[] millisByteArray = ByteUtils.longToByteArray(recoveredMillis);
System.arraycopy(millisByteArray, 0, byteArray, position, millisByteArray.length);
position = position + millisByteArray.length;
for (int i = 0; i < nativeArchiveNumber; i++) {
byte[] elementByteArray = nativeByteArray[i];
System.arraycopy(elementByteArray, 0, byteArray, position, elementByteArray.length);
position = position + elementByteArray.length;
}
if (optimizedArchiveNumber > 0) {
System.arraycopy(optimizedByteArray, 0, byteArray, position, optimizedByteArray.length);
position = position + optimizedByteArray.length;
}
for (int i = 0; i < remoteArchiveNumber; i++) {
byte[] elementByteArray = remoteByteArray[i];
System.arraycopy(elementByteArray, 0, byteArray, position, elementByteArray.length);
position = position + elementByteArray.length;
}
return byteArray;
}
public Object deserialize(TransactionXid xid, byte[] array) {
ByteBuffer buffer = ByteBuffer.wrap(array);
TransactionArchive archive = new TransactionArchive();
archive.setXid(xid);
int status = buffer.get();
int vote = buffer.get();
int coordinatorValue = buffer.get();
archive.setStatus(status);
archive.setVote(vote);
archive.setCoordinator(coordinatorValue != 0);
int nativeArchiveNumber = buffer.get();
int optimizedArchiveNumber = buffer.get();
int remoteArchiveNumber = buffer.get();
int transactionStrategyType = buffer.get();
archive.setTransactionStrategyType(transactionStrategyType);
byte[] hostByteArray = new byte[4];
buffer.get(hostByteArray);
StringBuilder ber = new StringBuilder();
for (int i = 0; i < hostByteArray.length; i++) {
int value = hostByteArray[i] + 128;
if (i == 0) {
ber.append(value);
} else {
ber.append(".");
ber.append(value);
}
}
String host = ber.toString();
int sizeOfName = 128 + buffer.get();
byte[] nameByteArray = new byte[sizeOfName];
buffer.get(nameByteArray);
String name = new String(nameByteArray);
int port = 32768 + buffer.getShort();
archive.setPropagatedBy(String.format("%s:%s:%s", host, name, port));
int recoveredTimes = 128 + buffer.get();
byte[] millisByteArray = new byte[8];
buffer.get(millisByteArray);
long recoveredAt = ByteUtils.byteArrayToLong(millisByteArray);
archive.setRecoveredTimes(recoveredTimes);
archive.setRecoveredAt(recoveredAt);
for (int i = 0; i < nativeArchiveNumber; i++) {
int length = buffer.getShort();
byte[] resourceByteArray = new byte[length];
buffer.get(resourceByteArray);
XAResourceArchive resourceArchive = //
(XAResourceArchive) this.resourceArchiveDeserializer.deserialize(xid, resourceByteArray);
archive.getNativeResources().add(resourceArchive);
}
if (optimizedArchiveNumber > 0) {
int length = buffer.getShort();
byte[] resourceByteArray = new byte[length];
buffer.get(resourceByteArray);
XAResourceArchive resourceArchive = //
(XAResourceArchive) this.resourceArchiveDeserializer.deserialize(xid, resourceByteArray);
archive.setOptimizedResource(resourceArchive);
}
for (int i = 0; i < remoteArchiveNumber; i++) {
int length = buffer.getShort();
byte[] resourceByteArray = new byte[length];
buffer.get(resourceByteArray);
XAResourceArchive resourceArchive = //
(XAResourceArchive) this.resourceArchiveDeserializer.deserialize(xid, resourceByteArray);
archive.getRemoteResources().add(resourceArchive);
}
return archive;
}
public ArchiveDeserializer getResourceArchiveDeserializer() {
return resourceArchiveDeserializer;
}
public void setResourceArchiveDeserializer(ArchiveDeserializer resourceArchiveDeserializer) {
this.resourceArchiveDeserializer = resourceArchiveDeserializer;
}
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/bytejta/logging/deserializer/XAResourceArchiveDeserializer.java
================================================
/**
* Copyright 2014-2016 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.bytejta.logging.deserializer;
import java.nio.ByteBuffer;
import javax.transaction.xa.Xid;
import org.bytesoft.bytejta.supports.resource.CommonResourceDescriptor;
import org.bytesoft.bytejta.supports.resource.LocalXAResourceDescriptor;
import org.bytesoft.bytejta.supports.resource.RemoteResourceDescriptor;
import org.bytesoft.bytejta.supports.resource.UnidentifiedResourceDescriptor;
import org.bytesoft.transaction.TransactionBeanFactory;
import org.bytesoft.transaction.archive.XAResourceArchive;
import org.bytesoft.transaction.aware.TransactionBeanFactoryAware;
import org.bytesoft.transaction.logging.ArchiveDeserializer;
import org.bytesoft.transaction.supports.resource.XAResourceDescriptor;
import org.bytesoft.transaction.supports.serialize.XAResourceDeserializer;
import org.bytesoft.transaction.xa.TransactionXid;
import org.bytesoft.transaction.xa.XidFactory;
public class XAResourceArchiveDeserializer implements ArchiveDeserializer, TransactionBeanFactoryAware {
@javax.inject.Inject
private TransactionBeanFactory beanFactory;
// private XAResourceDeserializer deserializer;
public byte[] serialize(TransactionXid xid, Object obj) {
XAResourceArchive archive = (XAResourceArchive) obj;
Xid branchXid = archive.getXid();
byte[] branchQualifier = branchXid.getBranchQualifier();
XAResourceDescriptor descriptor = archive.getDescriptor();
byte[] identifierByteArray = new byte[0];
byte typeByte = 0x0;
if (CommonResourceDescriptor.class.isInstance(descriptor)) {
typeByte = (byte) 0x1;
identifierByteArray = descriptor.getIdentifier().getBytes();
} else if (RemoteResourceDescriptor.class.isInstance(descriptor)) {
typeByte = (byte) 0x2;
identifierByteArray = descriptor.getIdentifier().getBytes();
} else if (LocalXAResourceDescriptor.class.isInstance(descriptor)) {
typeByte = (byte) 0x3;
identifierByteArray = descriptor.getIdentifier().getBytes();
}
byte branchVote = (byte) archive.getVote();
byte readonly = archive.isReadonly() ? (byte) 1 : (byte) 0;
byte committed = archive.isCommitted() ? (byte) 1 : (byte) 0;
byte rolledback = archive.isRolledback() ? (byte) 1 : (byte) 0;
byte completed = archive.isCompleted() ? (byte) 1 : (byte) 0;
byte heuristic = archive.isHeuristic() ? (byte) 1 : (byte) 0;
byte[] byteArray = new byte[XidFactory.BRANCH_QUALIFIER_LENGTH + 2 + identifierByteArray.length + 6];
System.arraycopy(branchQualifier, 0, byteArray, 0, branchQualifier.length);
byteArray[XidFactory.BRANCH_QUALIFIER_LENGTH] = typeByte;
byteArray[XidFactory.BRANCH_QUALIFIER_LENGTH + 1] = (byte) identifierByteArray.length;
if (identifierByteArray.length > 0) {
System.arraycopy(identifierByteArray, 0, byteArray, XidFactory.BRANCH_QUALIFIER_LENGTH + 2,
identifierByteArray.length);
}
byteArray[XidFactory.BRANCH_QUALIFIER_LENGTH + 2 + identifierByteArray.length] = branchVote;
byteArray[XidFactory.BRANCH_QUALIFIER_LENGTH + 2 + identifierByteArray.length + 1] = readonly;
byteArray[XidFactory.BRANCH_QUALIFIER_LENGTH + 2 + identifierByteArray.length + 2] = committed;
byteArray[XidFactory.BRANCH_QUALIFIER_LENGTH + 2 + identifierByteArray.length + 3] = rolledback;
byteArray[XidFactory.BRANCH_QUALIFIER_LENGTH + 2 + identifierByteArray.length + 4] = completed;
byteArray[XidFactory.BRANCH_QUALIFIER_LENGTH + 2 + identifierByteArray.length + 5] = heuristic;
return byteArray;
}
public Object deserialize(TransactionXid xid, byte[] array) {
XAResourceDeserializer deserializer = this.beanFactory.getResourceDeserializer();
ByteBuffer buffer = ByteBuffer.wrap(array);
XAResourceArchive archive = new XAResourceArchive();
byte[] branchQualifier = new byte[XidFactory.BRANCH_QUALIFIER_LENGTH];
buffer.get(branchQualifier);
XidFactory xidFactory = this.beanFactory.getXidFactory();
TransactionXid branchXid = xidFactory.createBranchXid(xid, branchQualifier);
archive.setXid(branchXid);
byte resourceType = buffer.get();
byte length = buffer.get();
byte[] byteArray = new byte[length];
buffer.get(byteArray);
String identifier = new String(byteArray);
XAResourceDescriptor descriptor = null;
if (resourceType == 0x01) {
archive.setIdentified(true);
descriptor = deserializer.deserialize(identifier);
} else if (resourceType == 0x02) {
archive.setIdentified(true);
descriptor = deserializer.deserialize(identifier);
} else if (resourceType == 0x03) {
archive.setIdentified(true);
descriptor = deserializer.deserialize(identifier);
} else {
descriptor = new UnidentifiedResourceDescriptor();
}
if (CommonResourceDescriptor.class.isInstance(descriptor)) {
((CommonResourceDescriptor) descriptor).setRecoverXid(branchXid);
}
archive.setDescriptor(descriptor);
int branchVote = buffer.get();
int readonly = buffer.get();
int committedValue = buffer.get();
int rolledbackValue = buffer.get();
int completedValue = buffer.get();
int heuristicValue = buffer.get();
archive.setVote(branchVote);
archive.setReadonly(readonly != 0);
archive.setCommitted(committedValue != 0);
archive.setRolledback(rolledbackValue != 0);
archive.setCompleted(completedValue != 0);
archive.setHeuristic(heuristicValue != 0);
return archive;
}
public TransactionBeanFactory getBeanFactory() {
return this.beanFactory;
}
public void setBeanFactory(TransactionBeanFactory tbf) {
this.beanFactory = tbf;
}
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/bytejta/logging/store/VirtualLoggingFile.java
================================================
/**
* Copyright 2014-2016 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.bytejta.logging.store;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel.MapMode;
import java.util.Arrays;
import org.bytesoft.transaction.logging.store.VirtualLoggingTrigger;
import org.bytesoft.transaction.xa.XidFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class VirtualLoggingFile {
static final Logger logger = LoggerFactory.getLogger(VirtualLoggingFile.class);
static final long DEFAULT_SIZE = 1024 * 1024;
static final long INCREASE_SIZE = 1024 * 512;
static final int DEFAULT_MAJOR_VERSION = 0;
static final int DEFAULT_MINOR_VERSION = 2;
private MappedByteBuffer readable;
private MappedByteBuffer writable;
private RandomAccessFile raf;
private byte[] identifier;
private boolean initialized;
private int startIdx;
private int endIndex;
private boolean marked;
private boolean master;
private int majorVersion = DEFAULT_MAJOR_VERSION;
private int minorVersion = DEFAULT_MINOR_VERSION;
private VirtualLoggingTrigger trigger;
public VirtualLoggingFile(File file) throws IOException {
this(file, DEFAULT_MAJOR_VERSION, DEFAULT_MINOR_VERSION);
}
public VirtualLoggingFile(File file, int major, int minor) throws IOException {
this.majorVersion = major;
this.minorVersion = minor;
this.initialized = file.exists();
this.raf = new RandomAccessFile(file, "rw");
if (this.initialized == false) {
this.configMappedByteBuffer(DEFAULT_SIZE);
} else {
this.configMappedByteBuffer(this.raf.length());
}
}
public void clearMarkedFlag() {
int pos = this.writable.position();
this.writable.position(identifier.length + 2 + 8 + 4);
this.writable.put((byte) 0x0);
this.marked = false;
this.writable.position(pos);
}
public void fixSwitchError() {
int pos = this.writable.position();
this.writable.position(identifier.length + 2 + 8 + 4 + 1);
this.writable.put((byte) 0x1);
this.writable.position(identifier.length + 2 + 8 + 4);
this.writable.put((byte) 0x0);
this.marked = false;
this.master = true;
this.writable.position(pos);
}
public void initialize(boolean master) {
this.checkLoggingIdentifier();
this.checkLoggingVersion();
this.checkCreatedTime();
this.checkStartIndex();
this.checkMasterFlag(master);
this.checkModifiedTime();
this.checkEndIndex();
this.initialized = true;
}
private void checkLoggingIdentifier() {
byte[] array = new byte[identifier.length];
this.writable.position(0);
this.writable.get(array);
if (Arrays.equals(identifier, array)) {
// ignore
} else if (this.initialized == false) {
writable.position(0);
writable.put(identifier);
} else {
throw new IllegalStateException("Illegal file format!");
}
}
private void checkLoggingVersion() {
this.writable.position(identifier.length);
int major = this.writable.get();
int minor = this.writable.get();
if (major == this.majorVersion && minor == this.minorVersion) {
// ignore
} else if (this.initialized == false) {
writable.position(identifier.length);
writable.put((byte) this.majorVersion);
writable.put((byte) this.minorVersion);
} else {
throw new IllegalStateException("Incompatible version!");
}
}
private void checkCreatedTime() {
if (this.initialized == false) {
writable.position(identifier.length + 2);
writable.putLong(System.currentTimeMillis());
}
}
private void checkStartIndex() {
this.writable.position(identifier.length + 2 + 8);
int start = this.writable.getInt();
if (start == identifier.length + 2 + 8 + 4 + 2 + 8 + 4) {
this.startIdx = start;
} else if (this.initialized == false) {
this.startIdx = identifier.length + 2 + 8 + 4 + 2 + 8 + 4;
writable.position(identifier.length + 2 + 8);
writable.putInt(identifier.length + 2 + 8 + 4 + 2 + 8 + 4);
} else {
throw new IllegalStateException();
}
}
private void checkMasterFlag(boolean master) {
if (this.initialized == false) {
this.master = master;
this.marked = false;
writable.position(identifier.length + 2 + 8 + 4);
writable.put((byte) 0x0);
writable.put(master ? (byte) 0x1 : (byte) 0x0);
} else {
this.writable.position(identifier.length + 2 + 8 + 4);
this.marked = this.writable.get() == 0x1;
this.master = this.writable.get() == 0x1;
}
}
private void checkModifiedTime() {
if (this.initialized == false) {
writable.position(identifier.length + 2 + 8 + 4 + 2);
writable.putLong(System.currentTimeMillis());
}
}
private void checkEndIndex() {
if (this.initialized == false) {
this.endIndex = identifier.length + 2 + 8 + 4 + 2 + 8 + 4;
writable.position(identifier.length + 2 + 8 + 4 + 2 + 8);
writable.putInt(identifier.length + 2 + 8 + 4 + 2 + 8 + 4);
} else {
this.writable.position(identifier.length + 2 + 8 + 4 + 2 + 8);
this.endIndex = this.writable.getInt();
}
}
public void markAsMaster() {
this.writable.position(identifier.length + 2 + 8 + 4);
this.writable.put((byte) 0x1);
}
public void switchToMaster() {
this.writable.position(identifier.length + 2 + 8 + 4 + 1);
this.writable.put((byte) 0x1);
this.writable.position(identifier.length + 2 + 8 + 4);
this.writable.put((byte) 0x0);
this.master = true;
this.marked = false;
this.readable.position(this.startIdx);
}
public void switchToSlaver() {
this.writable.position(identifier.length + 2 + 8 + 4);
this.writable.put((byte) 0x0);
this.writable.put((byte) 0x0);
this.master = false;
this.marked = false;
writable.position(identifier.length + 2 + 8 + 4 + 2);
this.writable.putLong(System.currentTimeMillis());
this.endIndex = this.startIdx;
this.writable.putInt(this.endIndex);
}
public void prepareForReading() {
this.readable.position(this.startIdx);
}
public byte[] read() {
if (this.readable.position() < this.endIndex) {
int pos = this.readable.position();
this.readable.position(pos + XidFactory.GLOBAL_TRANSACTION_LENGTH + 1);
int size = this.readable.getInt();
byte[] byteArray = new byte[XidFactory.GLOBAL_TRANSACTION_LENGTH + 1 + 4 + size];
this.readable.position(pos);
this.readable.get(byteArray);
return byteArray;
} else {
return new byte[0];
}
}
public void write(byte[] byteArray) {
if (this.writable.capacity() < this.endIndex + byteArray.length) {
this.resizeMappedByteBuffer(this.endIndex + INCREASE_SIZE);
}
this.writable.position(this.endIndex);
this.writable.put(byteArray);
writable.position(identifier.length + 2 + 8 + 4 + 2);
this.writable.putLong(System.currentTimeMillis());
this.endIndex = this.endIndex + byteArray.length;
this.writable.putInt(this.endIndex);
int threshold = (this.writable.capacity() * 2) / 3;
if (this.endIndex > threshold) {
this.trigger.fireSwapImmediately();
}
}
private void resizeMappedByteBuffer(long size) {
try {
this.raf.setLength(this.endIndex + INCREASE_SIZE);
this.readable = this.raf.getChannel().map(MapMode.READ_ONLY, 0, size);
this.writable = this.raf.getChannel().map(MapMode.READ_WRITE, 0, size);
} catch (IOException ex) {
logger.error("Error occurred while resizing the logging file!", ex);
}
}
private void configMappedByteBuffer(long size) throws IOException {
this.readable = this.raf.getChannel().map(MapMode.READ_ONLY, 0, size);
this.writable = this.raf.getChannel().map(MapMode.READ_WRITE, 0, size);
}
public void flushImmediately() {
if (this.writable != null) {
this.writable.force();
}
}
public void closeQuietly() {
if (this.raf != null) {
try {
this.raf.close();
} catch (Exception ex) {
logger.debug(ex.getMessage(), ex);
}
}
}
public byte[] getIdentifier() {
return identifier;
}
public void setIdentifier(byte[] identifier) {
this.identifier = identifier;
}
public VirtualLoggingTrigger getTrigger() {
return trigger;
}
public void setTrigger(VirtualLoggingTrigger trigger) {
this.trigger = trigger;
}
public boolean isMarked() {
return marked;
}
public void setMarked(boolean marked) {
this.marked = marked;
}
public boolean isMaster() {
return master;
}
public void setMaster(boolean master) {
this.master = master;
}
public int getStartIdx() {
return startIdx;
}
public int getEndIndex() {
return endIndex;
}
public int getMajorVersion() {
return majorVersion;
}
public int getMinorVersion() {
return minorVersion;
}
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/bytejta/logging/store/VirtualLoggingSystemImpl.java
================================================
/**
* Copyright 2014-2016 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.bytejta.logging.store;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.resource.spi.work.Work;
import javax.transaction.xa.Xid;
import org.bytesoft.common.utils.ByteUtils;
import org.bytesoft.transaction.logging.store.VirtualLoggingKey;
import org.bytesoft.transaction.logging.store.VirtualLoggingListener;
import org.bytesoft.transaction.logging.store.VirtualLoggingRecord;
import org.bytesoft.transaction.logging.store.VirtualLoggingSystem;
import org.bytesoft.transaction.logging.store.VirtualLoggingTrigger;
import org.bytesoft.transaction.xa.XidFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class VirtualLoggingSystemImpl implements VirtualLoggingSystem, VirtualLoggingTrigger, Work {
static final Logger logger = LoggerFactory.getLogger(VirtualLoggingSystemImpl.class);
static final int COMPRESS_BATCH_SIZE = 10000;
private final Lock lock = new ReentrantLock();
private final Lock timingLock = new ReentrantLock();
private final Condition timingCondition = this.timingLock.newCondition();
private boolean released;
private File directory;
private VirtualLoggingFile master;
private VirtualLoggingFile slaver;
private boolean optimized = true;
private boolean initialized;
private int switchThreshold = 1024 * 1024 * 8;
private int switchInterval = 60;
public synchronized void construct() throws IOException {
if (this.initialized == false) {
this.initialize();
this.initialized = true;
}
}
private void initialize() throws IOException {
if (this.directory == null) {
this.directory = this.getDefaultDirectory();
}
if (this.directory.exists() == false) {
if (this.directory.mkdirs() == false) {
throw new RuntimeException(String.format("Failed to create directory %s!", this.directory.getAbsolutePath()));
}
}
File fmaster = new File(this.directory, String.format("%s1.log", this.getLoggingFilePrefix()));
File fslaver = new File(this.directory, String.format("%s2.log", this.getLoggingFilePrefix()));
VirtualLoggingFile masterMgr = this.createTransactionLogging(fmaster);
VirtualLoggingFile slaverMgr = this.createTransactionLogging(fslaver);
masterMgr.initialize(true);
slaverMgr.initialize(false);
this.initialize(masterMgr, slaverMgr);
this.flushAllIfNecessary();
}
private void initialize(VirtualLoggingFile prev, VirtualLoggingFile next) {
boolean prevMaster = prev.isMaster();
boolean nextMaster = next.isMaster();
if (prevMaster && nextMaster) {
throw new IllegalStateException();
} else if (prevMaster == false && nextMaster == false) {
this.fixSwitchError(prev, next);
} else if (prevMaster) {
this.master = prev;
this.slaver = next;
this.master.clearMarkedFlag();
this.slaver.clearMarkedFlag();
} else {
this.master = next;
this.slaver = prev;
this.master.clearMarkedFlag();
this.slaver.clearMarkedFlag();
}
}
private void fixSwitchError(VirtualLoggingFile prev, VirtualLoggingFile next) {
boolean prevMarked = prev.isMarked();
boolean nextMarked = next.isMarked();
if (prevMarked && nextMarked) {
throw new IllegalStateException();
} else if (prevMarked == false && nextMarked == false) {
throw new IllegalStateException();
} else if (prevMarked) {
prev.fixSwitchError();
this.master = prev;
this.slaver = next;
} else {
next.fixSwitchError();
this.master = next;
this.slaver = prev;
}
}
public void run() {
int lastEndIndex = this.master.getEndIndex();
while (this.released == false) {
try {
this.timingLock.lock();
this.timingCondition.await(this.switchInterval, TimeUnit.SECONDS);
} catch (Exception ex) {
logger.debug(ex.getMessage(), ex);
} finally {
this.timingLock.unlock();
}
int increment = this.master.getEndIndex() - lastEndIndex;
if (increment < this.switchThreshold) {
continue;
} // end-if (increasement < this.switchThreshold)
this.syncMasterAndSlaver();
this.swapMasterAndSlaver();
lastEndIndex = this.master.getEndIndex();
}
}
public void fireSwapImmediately() {
try {
this.timingLock.lock();
this.timingCondition.signalAll();
} finally {
this.timingLock.unlock();
}
}
public void traversal(VirtualLoggingListener listener) {
this.master.prepareForReading();
while (true) {
byte[] byteArray = null;
try {
byteArray = this.master.read();
} catch (RuntimeException rex) {
byteArray = new byte[0];
}
if (byteArray.length == 0) {
break;
}
byte[] keyByteArray = new byte[XidFactory.GLOBAL_TRANSACTION_LENGTH];
System.arraycopy(byteArray, 0, keyByteArray, 0, keyByteArray.length);
int operator = byteArray[keyByteArray.length];
byte[] valueByteArray = new byte[byteArray.length - XidFactory.GLOBAL_TRANSACTION_LENGTH - 1 - 4];
System.arraycopy(byteArray, XidFactory.GLOBAL_TRANSACTION_LENGTH + 1 + 4, valueByteArray, 0, valueByteArray.length);
VirtualLoggingKey xid = new VirtualLoggingKey();
xid.setGlobalTransactionId(keyByteArray);
VirtualLoggingRecord record = new VirtualLoggingRecord();
record.setIdentifier(xid);
record.setOperator(operator);
record.setContent(byteArray);
record.setValue(valueByteArray);
listener.recvOperation(record);
}
}
public void create(Xid xid, byte[] textByteArray) {
byte[] keyByteArray = xid.getGlobalTransactionId();
byte[] sizeByteArray = ByteUtils.intToByteArray(textByteArray.length);
byte[] byteArray = new byte[keyByteArray.length + 1 + sizeByteArray.length + textByteArray.length];
System.arraycopy(keyByteArray, 0, byteArray, 0, keyByteArray.length);
byteArray[keyByteArray.length] = (byte) (OPERATOR_CREATE & 0xFF);
System.arraycopy(sizeByteArray, 0, byteArray, keyByteArray.length + 1, sizeByteArray.length);
System.arraycopy(textByteArray, 0, byteArray, keyByteArray.length + 1 + sizeByteArray.length, textByteArray.length);
try {
this.lock.lock();
this.master.write(byteArray);
this.flushMasterIfNecessary();
} finally {
this.lock.unlock();
}
}
public void delete(Xid xid) {
byte[] keyByteArray = xid.getGlobalTransactionId();
byte[] sizeByteArray = ByteUtils.intToByteArray(0);
byte[] byteArray = new byte[keyByteArray.length + 1 + sizeByteArray.length];
System.arraycopy(keyByteArray, 0, byteArray, 0, keyByteArray.length);
byteArray[keyByteArray.length] = (byte) (OPERATOR_DELETE & 0xFF);
System.arraycopy(sizeByteArray, 0, byteArray, keyByteArray.length + 1, sizeByteArray.length);
try {
this.lock.lock();
this.master.write(byteArray);
this.flushMasterIfNecessary();
} finally {
this.lock.unlock();
}
}
public void modify(Xid xid, byte[] textByteArray) {
byte[] keyByteArray = xid.getGlobalTransactionId();
byte[] sizeByteArray = ByteUtils.intToByteArray(textByteArray.length);
byte[] byteArray = new byte[keyByteArray.length + 1 + sizeByteArray.length + textByteArray.length];
System.arraycopy(keyByteArray, 0, byteArray, 0, keyByteArray.length);
byteArray[keyByteArray.length] = (byte) (OPERATOR_MOFIFY & 0xFF);
System.arraycopy(sizeByteArray, 0, byteArray, keyByteArray.length + 1, sizeByteArray.length);
System.arraycopy(textByteArray, 0, byteArray, keyByteArray.length + 1 + sizeByteArray.length, textByteArray.length);
try {
this.lock.lock();
this.master.write(byteArray);
this.flushMasterIfNecessary();
} finally {
this.lock.unlock();
}
}
public void syncMasterAndSlaver() {
this.master.prepareForReading();
Map recordMap = this.syncStepOne();
this.master.prepareForReading();
this.syncStepTwo(recordMap, true);
this.flushSlaverIfNecessary();
}
public Map syncStepOne() {
Map recordMap = new HashMap();
while (true) {
byte[] byteArray = null;
try {
byteArray = this.master.read();
} catch (RuntimeException rex) {
byteArray = new byte[0];
}
if (byteArray.length == 0) {
break;
}
byte[] keyByteArray = new byte[XidFactory.GLOBAL_TRANSACTION_LENGTH];
System.arraycopy(byteArray, 0, keyByteArray, 0, keyByteArray.length);
int operator = byteArray[keyByteArray.length];
VirtualLoggingKey xid = new VirtualLoggingKey();
xid.setGlobalTransactionId(keyByteArray);
VirtualLoggingRecord record = new VirtualLoggingRecord();
record.setIdentifier(xid);
record.setOperator(operator);
record.setContent(byteArray);
if (operator == OPERATOR_DELETE) {
recordMap.put(xid, true);
}
}
return recordMap;
}
public List compressIfNecessary(List recordList) {
return recordList;
}
public void syncStepTwo(Map recordMap, boolean compressRequired) {
final List recordList = new ArrayList(COMPRESS_BATCH_SIZE);
while (true) {
byte[] byteArray = null;
try {
byteArray = this.master.read();
} catch (RuntimeException rex) {
byteArray = new byte[0];
}
if (byteArray.length == 0) {
break;
}
byte[] keyByteArray = new byte[XidFactory.GLOBAL_TRANSACTION_LENGTH];
System.arraycopy(byteArray, 0, keyByteArray, 0, keyByteArray.length);
int operator = byteArray[keyByteArray.length];
byte[] valueByteArray = new byte[byteArray.length - XidFactory.GLOBAL_TRANSACTION_LENGTH - 1 - 4];
System.arraycopy(byteArray, XidFactory.GLOBAL_TRANSACTION_LENGTH + 1 + 4, valueByteArray, 0, valueByteArray.length);
VirtualLoggingKey xid = new VirtualLoggingKey();
xid.setGlobalTransactionId(keyByteArray);
if (recordMap.containsKey(xid)) /* deleted */ {
continue;
}
VirtualLoggingRecord record = new VirtualLoggingRecord();
record.setIdentifier(xid);
record.setOperator(operator);
record.setContent(byteArray);
record.setValue(valueByteArray);
recordList.add(record);
if (compressRequired == false) {
continue;
} else if (recordList.size() % COMPRESS_BATCH_SIZE != 0) {
continue;
}
List compressedList = this.compressIfNecessary(recordList);
if (compressedList != recordList && compressedList != null) {
recordList.clear();
recordList.addAll(compressedList);
} // end-if (compressedList != recordList && compressedList != null)
}
for (int i = 0; recordList != null && i < recordList.size(); i++) {
VirtualLoggingRecord record = recordList.get(i);
Xid xid = record.getIdentifier();
if (recordMap.containsKey(xid) == false) {
byte[] byteArray = record.getContent();
this.slaver.write(byteArray);
}
}
}
public void swapMasterAndSlaver() {
try {
this.lock.lock();
this.syncStepTwo(new HashMap(), false);
this.slaver.markAsMaster();
this.master.switchToSlaver();
this.slaver.switchToMaster();
this.flushAllIfNecessary();
VirtualLoggingFile theNextMaster = this.slaver;
this.slaver = this.master;
this.master = theNextMaster;
} finally {
this.lock.unlock();
}
}
private void flushAllIfNecessary() {
this.flushMasterIfNecessary();
this.flushSlaverIfNecessary();
}
private void flushMasterIfNecessary() {
if (this.optimized == false) {
this.master.flushImmediately();
}
}
private void flushSlaverIfNecessary() {
if (this.optimized == false) {
this.slaver.flushImmediately();
}
}
public void flushImmediately() {
this.master.flushImmediately();
}
public void shutdown() {
this.master.flushImmediately();
this.slaver.flushImmediately();
this.master.closeQuietly();
this.slaver.closeQuietly();
}
public void release() {
this.released = true;
}
public abstract File getDefaultDirectory();
public abstract int getMajorVersion();
public abstract int getMinorVersion();
public abstract String getLoggingIdentifier();
public abstract String getLoggingFilePrefix();
public VirtualLoggingFile createTransactionLogging(File file) throws IOException {
int major = this.getMajorVersion();
int minor = this.getMinorVersion();
VirtualLoggingFile logging = new VirtualLoggingFile(file, major, minor);
logging.setTrigger(this);
logging.setIdentifier(this.getLoggingIdentifier().getBytes());
return logging;
}
public int getSwitchThreshold() {
return switchThreshold;
}
public void setSwitchThreshold(int switchThreshold) {
this.switchThreshold = switchThreshold;
}
public int getSwitchInterval() {
return switchInterval;
}
public void setSwitchInterval(int switchInterval) {
this.switchInterval = switchInterval;
}
public boolean isOptimized() {
return optimized;
}
public void setOptimized(boolean optimized) {
this.optimized = optimized;
}
public File getDirectory() {
return directory;
}
public void setDirectory(File directory) {
this.directory = directory;
}
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/bytejta/resource/XATerminatorImpl.java
================================================
/**
* Copyright 2014-2016 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.bytejta.resource;
import java.util.ArrayList;
import java.util.List;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import org.bytesoft.common.utils.ByteUtils;
import org.bytesoft.transaction.TransactionBeanFactory;
import org.bytesoft.transaction.archive.XAResourceArchive;
import org.bytesoft.transaction.logging.TransactionLogger;
import org.bytesoft.transaction.resource.XATerminator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class XATerminatorImpl implements XATerminator {
static final Logger logger = LoggerFactory.getLogger(XATerminatorImpl.class);
private TransactionBeanFactory beanFactory;
private final List resources = new ArrayList();
public synchronized int prepare(Xid xid) throws XAException {
TransactionLogger transactionLogger = this.beanFactory.getTransactionLogger();
int globalVote = XAResource.XA_RDONLY;
for (int i = 0; i < this.resources.size(); i++) {
XAResourceArchive archive = this.resources.get(i);
boolean prepared = archive.getVote() != XAResourceArchive.DEFAULT_VOTE;
if (prepared) {
globalVote = archive.getVote() == XAResource.XA_RDONLY ? globalVote : XAResource.XA_OK;
} else {
int branchVote = archive.prepare(archive.getXid());
archive.setVote(branchVote);
if (branchVote == XAResource.XA_RDONLY) {
archive.setReadonly(true);
archive.setCompleted(true);
} else {
globalVote = XAResource.XA_OK;
}
transactionLogger.updateParticipant(archive);
}
logger.info("{}> prepare: xares= {}, branch= {}, vote= {}",
ByteUtils.byteArrayToString(archive.getXid().getGlobalTransactionId()), archive,
ByteUtils.byteArrayToString(archive.getXid().getBranchQualifier()), archive.getVote());
}
return globalVote;
}
/** error: XA_HEURHAZ, XA_HEURMIX, XA_HEURCOM, XA_HEURRB, XA_RDONLY, XAER_RMERR */
public synchronized void commit(Xid xid, boolean onePhase) throws XAException {
if (onePhase) {
this.fireOnePhaseCommit(xid);
} else {
this.fireTwoPhaseCommit(xid);
}
}
private void fireOnePhaseCommit(Xid xid) throws XAException {
if (this.resources.size() == 0) {
throw new XAException(XAException.XA_RDONLY);
} else if (this.resources.size() > 1) {
this.rollback(xid);
throw new XAException(XAException.XA_HEURRB);
}
TransactionLogger transactionLogger = this.beanFactory.getTransactionLogger();
XAResourceArchive archive = this.resources.get(0);
if (archive.isCommitted() && archive.isRolledback()) {
throw new XAException(XAException.XA_HEURMIX);
} else if (archive.isCommitted()) {
return;
} else if (archive.isReadonly()) {
throw new XAException(XAException.XA_RDONLY);
} else if (archive.isRolledback()) {
throw new XAException(XAException.XA_HEURRB);
}
boolean updateRequired = true;
try {
this.invokeOnePhaseCommit(archive);
archive.setCommitted(true);
archive.setCompleted(true);
logger.info("{}> commit: xares= {}, branch= {}, opc= {}",
ByteUtils.byteArrayToString(archive.getXid().getGlobalTransactionId()), archive,
ByteUtils.byteArrayToString(archive.getXid().getBranchQualifier()), true);
} catch (XAException xaex) {
logger.error("{}> Error occurred while committing xa-resource: xares= {}, branch= {}, code= {}",
ByteUtils.byteArrayToString(archive.getXid().getGlobalTransactionId()), archive,
ByteUtils.byteArrayToString(archive.getXid().getBranchQualifier()), xaex.errorCode, xaex);
switch (xaex.errorCode) {
case XAException.XA_HEURCOM:
archive.setHeuristic(true);
archive.setCommitted(true);
archive.setCompleted(true);
break;
case XAException.XA_HEURHAZ:
archive.setHeuristic(true);
throw xaex;
case XAException.XA_HEURMIX:
archive.setHeuristic(true);
archive.setCommitted(true);
archive.setRolledback(true);
archive.setCompleted(true);
throw xaex;
case XAException.XA_HEURRB:
archive.setHeuristic(true);
archive.setRolledback(true);
archive.setCompleted(true);
throw xaex;
case XAException.XAER_RMFAIL:
updateRequired = false;
throw new XAException(XAException.XA_HEURHAZ);
case XAException.XAER_RMERR:
default:
updateRequired = false;
throw new XAException(XAException.XAER_RMERR);
}
} catch (RuntimeException rex) {
logger.error("{}> Error occurred while committing xa-resource: xares= {}, branch= {}",
ByteUtils.byteArrayToString(archive.getXid().getGlobalTransactionId()), archive,
ByteUtils.byteArrayToString(archive.getXid().getBranchQualifier()), rex);
updateRequired = false;
throw new XAException(XAException.XA_HEURHAZ);
} finally {
if (updateRequired) {
transactionLogger.updateParticipant(archive);
}
}
}
private void fireTwoPhaseCommit(Xid xid) throws XAException {
TransactionLogger transactionLogger = this.beanFactory.getTransactionLogger();
boolean committedExists = false;
boolean rolledbackExists = false;
boolean unFinishExists = false;
boolean errorExists = false;
for (int i = this.resources.size() - 1; i >= 0; i--) {
XAResourceArchive archive = this.resources.get(i);
if (archive.isCommitted() && archive.isRolledback()) {
committedExists = true;
rolledbackExists = true;
continue;
} else if (archive.isCommitted()) {
committedExists = true;
continue;
} else if (archive.isReadonly()) {
continue;
} else if (archive.isRolledback()) {
rolledbackExists = true;
continue;
}
Xid branchXid = archive.getXid();
boolean updateRequired = true;
try {
this.invokeTwoPhaseCommit(archive);
committedExists = true;
archive.setCommitted(true);
archive.setCompleted(true);
logger.info("{}> commit: xares= {}, branch= {}, onePhaseCommit= {}",
ByteUtils.byteArrayToString(branchXid.getGlobalTransactionId()), archive,
ByteUtils.byteArrayToString(branchXid.getBranchQualifier()), false);
} catch (XAException xaex) {
logger.error("{}> Error occurred while committing xa-resource: xares= {}, branch= {}, code= {}",
ByteUtils.byteArrayToString(archive.getXid().getGlobalTransactionId()), archive,
ByteUtils.byteArrayToString(archive.getXid().getBranchQualifier()), xaex.errorCode, xaex);
switch (xaex.errorCode) {
case XAException.XA_HEURHAZ:
archive.setHeuristic(true);
unFinishExists = true;
break;
case XAException.XA_HEURMIX:
committedExists = true;
rolledbackExists = true;
archive.setCommitted(true);
archive.setRolledback(true);
archive.setHeuristic(true);
archive.setCompleted(true);
break;
case XAException.XA_HEURCOM:
committedExists = true;
archive.setCommitted(true);
archive.setHeuristic(true);
archive.setCompleted(true);
break;
case XAException.XA_HEURRB:
rolledbackExists = true;
archive.setRolledback(true);
archive.setHeuristic(true);
archive.setCompleted(true);
break;
case XAException.XAER_RMFAIL:
unFinishExists = true;
updateRequired = false;
break;
case XAException.XA_RDONLY:
archive.setReadonly(true);
break;
case XAException.XAER_RMERR:
default:
errorExists = true;
updateRequired = false;
}
} catch (RuntimeException rex) {
logger.error("{}> Error occurred while committing xa-resource: xares= {}, branch= {}",
ByteUtils.byteArrayToString(archive.getXid().getGlobalTransactionId()), archive,
ByteUtils.byteArrayToString(archive.getXid().getBranchQualifier()), rex);
unFinishExists = true;
updateRequired = false;
} finally {
if (updateRequired) {
transactionLogger.updateParticipant(archive);
}
}
} // end-for
if (committedExists && rolledbackExists) {
throw new XAException(XAException.XA_HEURMIX);
} else if (unFinishExists) {
throw new XAException(XAException.XA_HEURHAZ);
} else if (errorExists) {
throw new XAException(XAException.XAER_RMERR);
} else if (rolledbackExists) {
throw new XAException(XAException.XA_HEURRB);
} else if (committedExists == false) {
throw new XAException(XAException.XA_RDONLY);
}
}
private void invokeOnePhaseCommit(XAResourceArchive archive) throws XAException {
try {
archive.commit(archive.getXid(), true);
} catch (XAException xaex) {
switch (xaex.errorCode) {
case XAException.XA_HEURCOM:
case XAException.XA_HEURHAZ:
case XAException.XA_HEURMIX:
case XAException.XA_HEURRB:
logger.warn("An error occurred in one phase commit: {}, transaction has been completed!",
ByteUtils.byteArrayToString(archive.getXid().getGlobalTransactionId()));
throw xaex;
case XAException.XAER_RMFAIL:
logger.warn("An error occurred in one phase commit: {}",
ByteUtils.byteArrayToString(archive.getXid().getGlobalTransactionId()));
throw xaex;
case XAException.XAER_NOTA:
case XAException.XAER_INVAL:
case XAException.XAER_PROTO:
logger.warn("An error occurred in one phase commit: {}",
ByteUtils.byteArrayToString(archive.getXid().getGlobalTransactionId()));
throw new XAException(XAException.XAER_RMERR);
case XAException.XAER_RMERR:
case XAException.XA_RBCOMMFAIL:
case XAException.XA_RBDEADLOCK:
case XAException.XA_RBINTEGRITY:
case XAException.XA_RBOTHER:
case XAException.XA_RBPROTO:
case XAException.XA_RBROLLBACK:
case XAException.XA_RBTIMEOUT:
case XAException.XA_RBTRANSIENT:
default:
logger.warn("An error occurred in one phase commit: {}, transaction has been rolled back!",
ByteUtils.byteArrayToString(archive.getXid().getGlobalTransactionId()));
throw new XAException(XAException.XA_HEURRB);
}
}
}
private void invokeTwoPhaseCommit(XAResourceArchive archive) throws XAException {
try {
archive.commit(archive.getXid(), false);
} catch (XAException xaex) {
// * @exception XAException An error has occurred. Possible XAExceptions
// * are XA_HEURHAZ, XA_HEURCOM, XA_HEURRB, XA_HEURMIX, XAER_RMERR,
// * XAER_RMFAIL, XAER_NOTA, XAER_INVAL, or XAER_PROTO.
// *
If the resource manager did not commit the transaction and the
// * parameter onePhase is set to true, the resource manager may throw
// * one of the XA_RB* exceptions. Upon return, the resource manager has
// * rolled back the branch's work and has released all held resources.
switch (xaex.errorCode) {
case XAException.XA_HEURHAZ:
// OSI-TP: The condition that arises when, as a result of communication failure with a
// subordinate, the bound data of the subordinate's subtree are in an unknown state.
// XA: Due to some failure, the work done on behalf of the specified
// transaction branch may have been heuristically completed.
case XAException.XA_HEURMIX:
// Due to a heuristic decision, the work done on behalf of the specified
// transaction branch was partially committed and partially rolled back.
case XAException.XA_HEURCOM:
// Due to a heuristic decision, the work done on behalf of
// the specified transaction branch was committed.
case XAException.XA_HEURRB:
// Due to a heuristic decision, the work done on behalf of
// the specified transaction branch was rolled back.
throw xaex;
case XAException.XAER_NOTA:
// The specified XID is not known by the resource manager.
throw new XAException(XAException.XA_RDONLY); // read-only
case XAException.XAER_RMFAIL:
// An error occurred that makes the resource manager unavailable.
throw xaex;
case XAException.XAER_INVAL:
// Invalid arguments were specified.
case XAException.XAER_PROTO:
// The routine was invoked in an improper context.
XAException error = new XAException(XAException.XAER_RMERR);
error.initCause(xaex);
throw error;
case XAException.XAER_RMERR:
// An error occurred in committing the work performed on behalf of the transaction
// branch and the branch’s work has been rolled back. Note that returning this error
// signals a catastrophic event to a transaction manager since other resource
// managers may successfully commit their work on behalf of this branch. This error
// should be returned only when a resource manager concludes that it can never
// commit the branch and that it cannot hold the branch’s resources in a prepared
// state. Otherwise, [XA_RETRY] should be returned.
throw xaex; // TODO XA_RETRY is not defined by the JTA specification
default:// XA_RB*
XAException xarb = new XAException(XAException.XA_HEURRB);
xarb.initCause(xaex);
throw xarb;
}
}
}
/** error: XA_HEURHAZ, XA_HEURMIX, XA_HEURCOM, XA_HEURRB, XA_RDONLY, XAER_RMERR */
public synchronized void rollback(Xid xid) throws XAException {
TransactionLogger transactionLogger = this.beanFactory.getTransactionLogger();
boolean committedExists = false;
boolean rolledbackExists = false;
boolean unFinishExists = false;
boolean errorExists = false;
for (int i = 0; i < this.resources.size(); i++) {
XAResourceArchive archive = this.resources.get(i);
if (archive.isCommitted() && archive.isRolledback()) {
committedExists = true;
rolledbackExists = true;
continue;
} else if (archive.isRolledback()) {
rolledbackExists = true;
continue;
} else if (archive.isReadonly()) {
continue;
} else if (archive.isCommitted()) {
committedExists = true;
continue;
}
boolean updateRequired = true;
try {
this.invokeRollback(archive);
rolledbackExists = true;
archive.setRolledback(true);
archive.setCompleted(true);
logger.info("{}> rollback: xares= {}, branch= {}",
ByteUtils.byteArrayToString(archive.getXid().getGlobalTransactionId()), archive,
ByteUtils.byteArrayToString(archive.getXid().getBranchQualifier()));
} catch (XAException xaex) {
logger.error("{}> Error occurred while rolling back xa-resource: xares= {}, branch= {}, code= {}",
ByteUtils.byteArrayToString(archive.getXid().getGlobalTransactionId()), archive,
ByteUtils.byteArrayToString(archive.getXid().getBranchQualifier()), xaex.errorCode, xaex);
switch (xaex.errorCode) {
case XAException.XA_HEURHAZ:
unFinishExists = true;
archive.setHeuristic(true);
break;
case XAException.XA_HEURMIX:
committedExists = true;
rolledbackExists = true;
archive.setCommitted(true);
archive.setRolledback(true);
archive.setHeuristic(true);
archive.setCompleted(true);
break;
case XAException.XA_HEURCOM:
committedExists = true;
archive.setCommitted(true);
archive.setHeuristic(true);
archive.setCompleted(true);
break;
case XAException.XA_HEURRB:
rolledbackExists = true;
archive.setRolledback(true);
archive.setHeuristic(true);
archive.setCompleted(true);
break;
case XAException.XA_RDONLY:
archive.setReadonly(true);
archive.setCompleted(true);
break;
case XAException.XAER_RMFAIL:
unFinishExists = true;
updateRequired = false;
break;
case XAException.XAER_RMERR:
default:
errorExists = true;
updateRequired = false;
}
} catch (RuntimeException rex) {
unFinishExists = true;
updateRequired = false;
logger.error("{}> Error occurred while rolling back xa-resource: xares= {}, branch= {}",
ByteUtils.byteArrayToString(archive.getXid().getGlobalTransactionId()), archive,
ByteUtils.byteArrayToString(archive.getXid().getBranchQualifier()), rex);
} finally {
if (updateRequired) {
transactionLogger.updateParticipant(archive);
}
}
}
if (committedExists && rolledbackExists) {
throw new XAException(XAException.XA_HEURMIX);
} else if (unFinishExists) {
throw new XAException(XAException.XA_HEURHAZ);
} else if (errorExists) {
throw new XAException(XAException.XAER_RMERR);
} else if (committedExists) {
throw new XAException(XAException.XA_HEURCOM);
} else if (rolledbackExists == false) {
throw new XAException(XAException.XA_RDONLY);
}
}
private void invokeRollback(XAResourceArchive archive) throws XAException {
try {
archive.rollback(archive.getXid());
} catch (XAException xaex) {
// * @exception XAException An error has occurred. Possible XAExceptions are
// * XA_HEURHAZ, XA_HEURCOM, XA_HEURRB, XA_HEURMIX, XAER_RMERR, XAER_RMFAIL,
// * XAER_NOTA, XAER_INVAL, or XAER_PROTO.
// *
If the transaction branch is already marked rollback-only the
// * resource manager may throw one of the XA_RB* exceptions. Upon return,
// * the resource manager has rolled back the branch's work and has released
// * all held resources.
switch (xaex.errorCode) {
case XAException.XA_HEURHAZ:
// Due to some failure, the work done on behalf of the specified transaction branch
// may have been heuristically completed. A resource manager may return this
// value only if it has successfully prepared xid.
case XAException.XA_HEURMIX:
// Due to a heuristic decision, the work done on behalf of the specified transaction
// branch was partially committed and partially rolled back. A resource manager
// may return this value only if it has successfully prepared xid.
case XAException.XA_HEURCOM:
// Due to a heuristic decision, the work done on behalf of the specified transaction
// branch was committed. A resource manager may return this value only if it has
// successfully prepared xid.
case XAException.XA_HEURRB:
// Due to a heuristic decision, the work done on behalf of the specified transaction
// branch was rolled back. A resource manager may return this value only if it has
// successfully prepared xid.
throw xaex;
case XAException.XAER_RMFAIL:
// An error occurred that makes the resource manager unavailable.
XAException xrhaz = new XAException(XAException.XA_HEURHAZ);
xrhaz.initCause(xaex);
throw xrhaz;
case XAException.XAER_NOTA:
// The specified XID is not known by the resource manager.
if (archive.isReadonly()) {
throw new XAException(XAException.XA_RDONLY);
} else if (archive.getVote() == XAResourceArchive.DEFAULT_VOTE) {
break; // rolled back
} else if (archive.getVote() == XAResource.XA_RDONLY) {
throw new XAException(XAException.XA_RDONLY);
} else if (archive.getVote() == XAResource.XA_OK) {
throw new XAException(XAException.XAER_RMERR);
} else {
throw new XAException(XAException.XAER_RMERR);
}
case XAException.XAER_PROTO:
// The routine was invoked in an improper context.
case XAException.XAER_INVAL:
// Invalid arguments were specified.
throw new XAException(XAException.XAER_RMERR);
case XAException.XAER_RMERR:
// An error occurred in rolling back the transaction branch. The resource manager is
// free to forget about the branch when returning this error so long as all accessing
// threads of control have been notified of the branch’s state.
default: // XA_RB*
// The resource manager has rolled back the transaction branch’s work and has
// released all held resources. These values are typically returned when the
// branch was already marked rollback-only.
XAException xarb = new XAException(XAException.XA_HEURRB);
xarb.initCause(xaex);
throw xarb;
}
}
}
public int getTransactionTimeout() throws XAException {
throw new XAException(XAException.XAER_RMFAIL);
}
public boolean setTransactionTimeout(int seconds) throws XAException {
throw new XAException(XAException.XAER_RMFAIL);
}
public void start(Xid xid, int flags) throws XAException {
throw new XAException(XAException.XAER_RMFAIL);
}
public void end(Xid xid, int flags) throws XAException {
throw new XAException(XAException.XAER_RMFAIL);
}
public boolean isSameRM(XAResource xares) throws XAException {
throw new XAException(XAException.XAER_RMFAIL);
}
public Xid[] recover(int flag) throws XAException {
throw new XAException(XAException.XAER_RMFAIL);
}
public void forget(Xid xid) throws XAException {
for (int i = 0; i < this.resources.size(); i++) {
XAResourceArchive archive = this.resources.get(i);
Xid currentXid = archive.getXid();
if (archive.isHeuristic() == false) {
continue;
}
try {
Xid branchXid = archive.getXid();
archive.forget(branchXid);
} catch (XAException xae) {
// Possible exception values are XAER_RMERR, XAER_RMFAIL
// , XAER_NOTA, XAER_INVAL, or XAER_PROTO.
switch (xae.errorCode) {
case XAException.XAER_RMERR:
logger.error("{}> forget: xares= {}, branch={}, error= {}",
ByteUtils.byteArrayToString(currentXid.getGlobalTransactionId()), archive,
ByteUtils.byteArrayToString(currentXid.getBranchQualifier()), xae.errorCode);
break;
case XAException.XAER_RMFAIL:
logger.error("{}> forget: xares= {}, branch={}, error= {}",
ByteUtils.byteArrayToString(currentXid.getGlobalTransactionId()), archive,
ByteUtils.byteArrayToString(currentXid.getBranchQualifier()), xae.errorCode);
break;
case XAException.XAER_NOTA:
case XAException.XAER_INVAL:
case XAException.XAER_PROTO:
break;
default:
logger.error("{}> forget: xares= {}, branch={}, error= {}",
ByteUtils.byteArrayToString(currentXid.getGlobalTransactionId()), archive,
ByteUtils.byteArrayToString(currentXid.getBranchQualifier()), xae.errorCode);
}
}
} // end-for
}
public List getResourceArchives() {
return this.resources;
}
public TransactionBeanFactory getBeanFactory() {
return beanFactory;
}
public void setBeanFactory(TransactionBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/bytejta/resource/XATerminatorOptd.java
================================================
/**
* Copyright 2014-2016 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.bytejta.resource;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import org.bytesoft.common.utils.ByteUtils;
import org.bytesoft.transaction.TransactionBeanFactory;
import org.bytesoft.transaction.archive.XAResourceArchive;
import org.bytesoft.transaction.logging.TransactionLogger;
import org.bytesoft.transaction.resource.XATerminator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class XATerminatorOptd implements XATerminator {
static final Logger logger = LoggerFactory.getLogger(XATerminatorOptd.class);
private TransactionBeanFactory beanFactory;
private XAResourceArchive archive;
public synchronized int prepare(Xid xid) throws XAException {
if (this.archive == null) {
return XAResource.XA_RDONLY;
}
TransactionLogger transactionLogger = this.beanFactory.getTransactionLogger();
boolean prepared = archive.getVote() != XAResourceArchive.DEFAULT_VOTE;
int globalVote = XAResource.XA_RDONLY;
if (prepared) {
globalVote = archive.getVote();
} else {
globalVote = archive.prepare(archive.getXid());
archive.setVote(globalVote);
if (globalVote == XAResource.XA_RDONLY) {
archive.setReadonly(true);
archive.setCompleted(true);
} else {
globalVote = XAResource.XA_OK;
}
transactionLogger.updateParticipant(archive);
}
logger.info("{}> prepare: xares= {}, branch= {}, vote= {}",
ByteUtils.byteArrayToString(this.archive.getXid().getGlobalTransactionId()), archive,
ByteUtils.byteArrayToString(this.archive.getXid().getBranchQualifier()), globalVote);
return globalVote;
}
/** error: XA_HEURHAZ, XA_HEURMIX, XA_HEURCOM, XA_HEURRB, XA_RDONLY, XAER_RMERR */
public synchronized void commit(Xid xid, boolean onePhase) throws XAException {
if (onePhase) {
this.fireOnePhaseCommit(xid);
} else {
this.fireTwoPhaseCommit(xid);
}
}
private void fireOnePhaseCommit(Xid xid) throws XAException {
if (archive.isCommitted() && archive.isRolledback()) {
throw new XAException(XAException.XA_HEURMIX);
} else if (archive.isCommitted()) {
return;
} else if (archive.isReadonly()) {
throw new XAException(XAException.XA_RDONLY); // XAException.XAER_NOTA
} else if (archive.isRolledback()) {
throw new XAException(XAException.XA_HEURRB);
}
TransactionLogger transactionLogger = this.beanFactory.getTransactionLogger();
boolean updateRequired = true;
try {
archive.commit(archive.getXid(), true);
archive.setCommitted(true);
archive.setCompleted(true);
logger.info("{}> commit: xares= {}, branch= {}, opc= {}",
ByteUtils.byteArrayToString(archive.getXid().getGlobalTransactionId()), archive,
ByteUtils.byteArrayToString(archive.getXid().getBranchQualifier()), false);
} catch (XAException xaex) {
switch (xaex.errorCode) {
case XAException.XA_HEURCOM:
archive.setHeuristic(true);
archive.setCommitted(true);
archive.setCompleted(true);
break;
case XAException.XA_HEURMIX:
archive.setHeuristic(true);
archive.setCommitted(true);
archive.setRolledback(true);
archive.setCompleted(true);
throw xaex;
case XAException.XA_HEURRB:
archive.setHeuristic(true);
archive.setRolledback(true);
archive.setCompleted(true);
throw xaex;
case XAException.XA_HEURHAZ:
archive.setHeuristic(true);
throw xaex;
case XAException.XAER_RMFAIL:
logger.warn("An error occurred in one phase commit: {}",
ByteUtils.byteArrayToString(archive.getXid().getGlobalTransactionId()));
updateRequired = false;
throw new XAException(XAException.XA_HEURHAZ);
case XAException.XAER_NOTA:
case XAException.XAER_INVAL:
case XAException.XAER_PROTO:
logger.warn("An error occurred in one phase commit: {}",
ByteUtils.byteArrayToString(archive.getXid().getGlobalTransactionId()));
updateRequired = false;
throw new XAException(XAException.XAER_RMERR);
case XAException.XAER_RMERR:
case XAException.XA_RBCOMMFAIL:
case XAException.XA_RBDEADLOCK:
case XAException.XA_RBINTEGRITY:
case XAException.XA_RBOTHER:
case XAException.XA_RBPROTO:
case XAException.XA_RBROLLBACK:
case XAException.XA_RBTIMEOUT:
case XAException.XA_RBTRANSIENT:
default:
logger.warn("An error occurred in one phase commit: {}, transaction has been rolled back!",
ByteUtils.byteArrayToString(archive.getXid().getGlobalTransactionId()));
archive.setRolledback(true);
archive.setCompleted(true);
throw new XAException(XAException.XA_HEURRB);
}
} catch (RuntimeException rex) {
logger.error("{}> Error occurred while committing xa-resource: xares= {}, branch= {}",
ByteUtils.byteArrayToString(archive.getXid().getGlobalTransactionId()), archive,
ByteUtils.byteArrayToString(archive.getXid().getBranchQualifier()), rex);
updateRequired = false;
throw new XAException(XAException.XA_HEURHAZ);
} finally {
if (updateRequired) {
transactionLogger.updateParticipant(archive);
}
}
}
private void fireTwoPhaseCommit(Xid xid) throws XAException {
if (archive.isCommitted() && archive.isRolledback()) {
throw new XAException(XAException.XA_HEURMIX);
} else if (archive.isCommitted()) {
return;
} else if (archive.isReadonly()) {
throw new XAException(XAException.XA_RDONLY); // XAException.XAER_NOTA
} else if (archive.isRolledback()) {
throw new XAException(XAException.XA_HEURRB);
}
TransactionLogger transactionLogger = this.beanFactory.getTransactionLogger();
boolean updateRequired = true;
try {
archive.commit(archive.getXid(), false);
archive.setCommitted(true);
archive.setCompleted(true);
logger.info("{}> commit: xares= {}, branch= {}, opc= {}",
ByteUtils.byteArrayToString(archive.getXid().getGlobalTransactionId()), archive,
ByteUtils.byteArrayToString(archive.getXid().getBranchQualifier()), false);
} catch (XAException xaex) {
// * @exception XAException An error has occurred. Possible XAExceptions
// * are XA_HEURHAZ, XA_HEURCOM, XA_HEURRB, XA_HEURMIX, XAER_RMERR,
// * XAER_RMFAIL, XAER_NOTA, XAER_INVAL, or XAER_PROTO.
// *
If the resource manager did not commit the transaction and the
// * parameter onePhase is set to true, the resource manager may throw
// * one of the XA_RB* exceptions. Upon return, the resource manager has
// * rolled back the branch's work and has released all held resources.
switch (xaex.errorCode) {
case XAException.XA_HEURHAZ:
// OSI-TP: The condition that arises when, as a result of communication failure with a
// subordinate, the bound data of the subordinate's subtree are in an unknown state.
// XA: Due to some failure, the work done on behalf of the specified
// transaction branch may have been heuristically completed.
archive.setHeuristic(true);
throw xaex;
case XAException.XA_HEURMIX:
// Due to a heuristic decision, the work done on behalf of the specified
// transaction branch was partially committed and partially rolled back.
archive.setHeuristic(true);
archive.setCommitted(true);
archive.setRolledback(true);
archive.setCompleted(true);
throw xaex;
case XAException.XA_HEURCOM:
// Due to a heuristic decision, the work done on behalf of
// the specified transaction branch was committed.
archive.setHeuristic(true);
archive.setCommitted(true);
archive.setCompleted(true);
break;
case XAException.XA_HEURRB:
// Due to a heuristic decision, the work done on behalf of
// the specified transaction branch was rolled back.
archive.setHeuristic(true);
archive.setRolledback(true);
archive.setCompleted(true);
throw xaex;
case XAException.XAER_NOTA:
// The specified XID is not known by the resource manager.
archive.setReadonly(true);
throw new XAException(XAException.XA_RDONLY); // read-only
case XAException.XAER_RMFAIL:
// An error occurred that makes the resource manager unavailable.
updateRequired = false;
XAException xahaz = new XAException(XAException.XA_HEURHAZ);
xahaz.initCause(xaex);
throw xahaz;
case XAException.XAER_INVAL:
// Invalid arguments were specified.
case XAException.XAER_PROTO:
// The routine was invoked in an improper context.
updateRequired = false;
XAException error = new XAException(XAException.XAER_RMERR);
error.initCause(xaex);
throw error;
case XAException.XAER_RMERR:
// An error occurred in committing the work performed on behalf of the transaction
// branch and the branch’s work has been rolled back. Note that returning this error
// signals a catastrophic event to a transaction manager since other resource
// managers may successfully commit their work on behalf of this branch. This error
// should be returned only when a resource manager concludes that it can never
// commit the branch and that it cannot hold the branch’s resources in a prepared
// state. Otherwise, [XA_RETRY] should be returned.
default: // XA_RB*
archive.setRolledback(true);
archive.setCompleted(true);
XAException xarb = new XAException(XAException.XA_HEURRB);
xarb.initCause(xaex);
throw xarb;
}
} catch (RuntimeException rex) {
logger.error("{}> Error occurred while committing xa-resource: xares= {}, branch= {}",
ByteUtils.byteArrayToString(archive.getXid().getGlobalTransactionId()), archive,
ByteUtils.byteArrayToString(archive.getXid().getBranchQualifier()), rex);
updateRequired = false;
throw new XAException(XAException.XA_HEURHAZ);
} finally {
if (updateRequired) {
transactionLogger.updateParticipant(archive);
}
}
}
/** error: XA_HEURHAZ, XA_HEURMIX, XA_HEURCOM, XA_HEURRB, XA_RDONLY, XAER_RMERR */
public synchronized void rollback(Xid xid) throws XAException {
if (archive.isCommitted() && archive.isRolledback()) {
throw new XAException(XAException.XA_HEURMIX);
} else if (archive.isRolledback()) {
return;
} else if (archive.isReadonly()) {
throw new XAException(XAException.XA_RDONLY);
} else if (archive.isCommitted()) {
throw new XAException(XAException.XA_HEURCOM);
}
TransactionLogger transactionLogger = this.beanFactory.getTransactionLogger();
boolean updateRequired = true;
try {
archive.rollback(archive.getXid());
archive.setRolledback(true);
archive.setCompleted(true);
logger.info("{}> rollback: xares= {}, branch= {}",
ByteUtils.byteArrayToString(archive.getXid().getGlobalTransactionId()), archive,
ByteUtils.byteArrayToString(archive.getXid().getBranchQualifier()));
} catch (XAException xaex) {
logger.error("{}> Error occurred while rolling back xa-resource: xares= {}, branch= {}, code= {}",
ByteUtils.byteArrayToString(archive.getXid().getGlobalTransactionId()), archive,
ByteUtils.byteArrayToString(archive.getXid().getBranchQualifier()), xaex.errorCode, xaex);
// * @exception XAException An error has occurred. Possible XAExceptions are
// * XA_HEURHAZ, XA_HEURCOM, XA_HEURRB, XA_HEURMIX, XAER_RMERR, XAER_RMFAIL,
// * XAER_NOTA, XAER_INVAL, or XAER_PROTO.
// *
If the transaction branch is already marked rollback-only the
// * resource manager may throw one of the XA_RB* exceptions. Upon return,
// * the resource manager has rolled back the branch's work and has released
// * all held resources.
switch (xaex.errorCode) {
case XAException.XA_HEURHAZ:
// Due to some failure, the work done on behalf of the specified transaction branch
// may have been heuristically completed. A resource manager may return this
// value only if it has successfully prepared xid.
archive.setHeuristic(true);
throw xaex;
case XAException.XA_HEURMIX:
// Due to a heuristic decision, the work done on behalf of the specified transaction
// branch was partially committed and partially rolled back. A resource manager
// may return this value only if it has successfully prepared xid.
archive.setHeuristic(true);
archive.setCommitted(true);
archive.setRolledback(true);
archive.setCompleted(true);
throw xaex;
case XAException.XA_HEURCOM:
// Due to a heuristic decision, the work done on behalf of the specified transaction
// branch was committed. A resource manager may return this value only if it has
// successfully prepared xid.
archive.setHeuristic(true);
archive.setCommitted(true);
archive.setCompleted(true);
throw xaex;
case XAException.XA_HEURRB:
// Due to a heuristic decision, the work done on behalf of the specified transaction
// branch was rolled back. A resource manager may return this value only if it has
// successfully prepared xid.
archive.setHeuristic(true);
archive.setRolledback(true);
archive.setCompleted(true);
break;
case XAException.XAER_RMFAIL:
// An error occurred that makes the resource manager unavailable.
updateRequired = false;
throw new XAException(XAException.XA_HEURHAZ);
case XAException.XAER_NOTA:
// The specified XID is not known by the resource manager.
if (archive.isReadonly()) {
archive.setReadonly(true);
archive.setCompleted(true);
throw new XAException(XAException.XA_RDONLY);
} else if (archive.getVote() == XAResourceArchive.DEFAULT_VOTE) {
archive.setRolledback(true);
archive.setCompleted(true);
break; // rolled back
} else if (archive.getVote() == XAResource.XA_RDONLY) {
archive.setReadonly(true);
archive.setCompleted(true);
throw new XAException(XAException.XA_RDONLY);
} else if (archive.getVote() == XAResource.XA_OK) {
updateRequired = false;
throw new XAException(XAException.XAER_RMERR);
} else {
updateRequired = false;
throw new XAException(XAException.XAER_RMERR);
}
case XAException.XAER_PROTO:
// The routine was invoked in an improper context.
case XAException.XAER_INVAL:
// Invalid arguments were specified.
updateRequired = false;
throw new XAException(XAException.XAER_RMERR);
case XAException.XAER_RMERR:
// An error occurred in rolling back the transaction branch. The resource manager is
// free to forget about the branch when returning this error so long as all accessing
// threads of control have been notified of the branch’s state.
default: // XA_RB*
// The resource manager has rolled back the transaction branch’s work and has
// released all held resources. These values are typically returned when the
// branch was already marked rollback-only.
archive.setRolledback(true);
archive.setCompleted(true);
}
} catch (RuntimeException rex) {
logger.error("{}> Error occurred while rolling back xa-resource: xares= {}, branch= {}",
ByteUtils.byteArrayToString(archive.getXid().getGlobalTransactionId()), archive,
ByteUtils.byteArrayToString(archive.getXid().getBranchQualifier()), rex);
updateRequired = false;
throw new XAException(XAException.XA_HEURHAZ);
} finally {
if (updateRequired) {
transactionLogger.updateParticipant(archive);
}
}
}
public int getTransactionTimeout() throws XAException {
throw new XAException(XAException.XAER_RMFAIL);
}
public boolean setTransactionTimeout(int seconds) throws XAException {
throw new XAException(XAException.XAER_RMFAIL);
}
public void start(Xid xid, int flags) throws XAException {
throw new XAException(XAException.XAER_RMFAIL);
}
public void end(Xid xid, int flags) throws XAException {
throw new XAException(XAException.XAER_RMFAIL);
}
public boolean isSameRM(XAResource xares) throws XAException {
throw new XAException(XAException.XAER_RMFAIL);
}
public Xid[] recover(int flag) throws XAException {
throw new XAException(XAException.XAER_RMFAIL);
}
public void forget(Xid xid) throws XAException {
if (this.archive == null) {
return;
}
Xid currentXid = archive.getXid();
if (archive.isHeuristic()) {
try {
Xid branchXid = archive.getXid();
archive.forget(branchXid);
} catch (XAException xae) {
// Possible exception values are XAER_RMERR, XAER_RMFAIL
// , XAER_NOTA, XAER_INVAL, or XAER_PROTO.
switch (xae.errorCode) {
case XAException.XAER_RMERR:
logger.error("{}> forget: xares= {}, branch={}, error= {}",
ByteUtils.byteArrayToString(currentXid.getGlobalTransactionId()), archive,
ByteUtils.byteArrayToString(currentXid.getBranchQualifier()), xae.errorCode);
break;
case XAException.XAER_RMFAIL:
logger.error("{}> forget: xares= {}, branch={}, error= {}",
ByteUtils.byteArrayToString(currentXid.getGlobalTransactionId()), archive,
ByteUtils.byteArrayToString(currentXid.getBranchQualifier()), xae.errorCode);
break;
case XAException.XAER_NOTA:
case XAException.XAER_INVAL:
case XAException.XAER_PROTO:
break;
default:
logger.error("{}> forget: xares= {}, branch={}, error= {}",
ByteUtils.byteArrayToString(currentXid.getGlobalTransactionId()), archive,
ByteUtils.byteArrayToString(currentXid.getBranchQualifier()), xae.errorCode);
}
}
} // end-if
}
public List getResourceArchives() {
return new List() {
public int size() {
return archive == null ? 0 : 1;
}
public boolean isEmpty() {
return archive == null ? true : false;
}
public boolean contains(Object o) {
throw new IllegalStateException("Not supported yet!");
}
public Iterator iterator() {
throw new IllegalStateException("Not supported yet!");
}
public Object[] toArray() {
return archive == null ? new Object[0] : new Object[] { archive };
}
public T[] toArray(T[] a) {
throw new IllegalStateException("Not supported yet!");
}
public boolean add(XAResourceArchive e) {
if (XATerminatorOptd.this.archive != null && e != null) {
throw new IllegalStateException("Not supported yet!");
} else if (e != null) {
XATerminatorOptd.this.archive = e;
return true;
} else {
throw new IllegalStateException("Not supported yet!");
}
}
public boolean remove(Object o) {
throw new IllegalStateException("Not supported yet!");
}
public boolean containsAll(Collection> c) {
throw new IllegalStateException("Not supported yet!");
}
public boolean addAll(Collection extends XAResourceArchive> c) {
if (c == null || c.size() > 1) {
throw new IllegalStateException("Not supported yet!");
} else if (XATerminatorOptd.this.archive != null && c.isEmpty() == false) {
throw new IllegalStateException("Not supported yet!");
} else if (c.isEmpty() == false) {
Object[] array = c.toArray();
XATerminatorOptd.this.archive = (XAResourceArchive) array[0];
}
return true;
}
public boolean addAll(int index, Collection extends XAResourceArchive> c) {
throw new IllegalStateException("Not supported yet!");
}
public boolean removeAll(Collection> c) {
throw new IllegalStateException("Not supported yet!");
}
public boolean retainAll(Collection> c) {
throw new IllegalStateException("Not supported yet!");
}
public void clear() {
throw new IllegalStateException("Not supported yet!");
}
public XAResourceArchive get(int index) {
if (index > 0 || index < 0) {
throw new IndexOutOfBoundsException(String.format("index: %s, size: %s", index, this.size()));
} else if (archive == null) {
throw new IndexOutOfBoundsException(String.format("index: %s, size: 0", index));
}
return archive;
}
public XAResourceArchive set(int index, XAResourceArchive element) {
throw new IllegalStateException("Not supported yet!");
}
public void add(int index, XAResourceArchive element) {
throw new IllegalStateException("Not supported yet!");
}
public XAResourceArchive remove(int index) {
throw new IllegalStateException("Not supported yet!");
}
public int indexOf(Object o) {
throw new IllegalStateException("Not supported yet!");
}
public int lastIndexOf(Object o) {
throw new IllegalStateException("Not supported yet!");
}
public ListIterator listIterator() {
throw new IllegalStateException("Not supported yet!");
}
public ListIterator listIterator(int index) {
throw new IllegalStateException("Not supported yet!");
}
public List subList(int fromIndex, int toIndex) {
throw new IllegalStateException("Not supported yet!");
}
};
}
public TransactionBeanFactory getBeanFactory() {
return beanFactory;
}
public void setBeanFactory(TransactionBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/bytejta/strategy/CommonTransactionStrategy.java
================================================
/**
* Copyright 2014-2017 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.bytejta.strategy;
import javax.transaction.HeuristicCommitException;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.SystemException;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import org.bytesoft.bytejta.TransactionStrategy;
import org.bytesoft.transaction.CommitRequiredException;
import org.bytesoft.transaction.RollbackRequiredException;
import org.bytesoft.transaction.resource.XATerminator;
public class CommonTransactionStrategy implements TransactionStrategy {
private final XATerminator nativeTerminator;
private final XATerminator remoteTerminator;
public CommonTransactionStrategy(XATerminator nativeTerminator, XATerminator remoteTerminator) {
if (nativeTerminator == null || nativeTerminator.getResourceArchives().isEmpty()) {
throw new IllegalStateException();
} else if (remoteTerminator == null || remoteTerminator.getResourceArchives().isEmpty()) {
throw new IllegalStateException();
}
this.nativeTerminator = nativeTerminator;
this.remoteTerminator = remoteTerminator;
}
public int prepare(Xid xid) throws RollbackRequiredException, CommitRequiredException {
int nativeVote = XAResource.XA_RDONLY;
try {
nativeVote = this.nativeTerminator.prepare(xid);
} catch (Exception ex) {
throw new RollbackRequiredException();
}
int remoteVote = XAResource.XA_RDONLY;
try {
remoteVote = this.remoteTerminator.prepare(xid);
} catch (Exception ex) {
throw new RollbackRequiredException();
}
if (XAResource.XA_OK == nativeVote || XAResource.XA_OK == remoteVote) {
return XAResource.XA_OK;
} else {
return XAResource.XA_RDONLY;
}
}
public void commit(Xid xid, boolean onePhaseCommit)
throws HeuristicMixedException, HeuristicRollbackException, IllegalStateException, SystemException {
boolean committedExists = false;
boolean rolledbackExists = false;
boolean unFinishExists = false;
boolean errorExists = false;
try {
this.nativeTerminator.commit(xid, onePhaseCommit);
committedExists = true;
} catch (XAException ex) {
// error: XA_HEURHAZ, XA_HEURMIX, XA_HEURCOM, XA_HEURRB, XA_RDONLY, XAER_RMERR
switch (ex.errorCode) {
case XAException.XA_HEURCOM:
committedExists = true;
break;
case XAException.XA_HEURRB:
rolledbackExists = true;
break;
case XAException.XA_HEURMIX:
committedExists = true;
rolledbackExists = true;
break;
case XAException.XA_HEURHAZ:
unFinishExists = true;
break;
case XAException.XA_RDONLY:
break;
case XAException.XAER_RMERR:
errorExists = true;
break;
default:
// should never happen
errorExists = true;
}
} catch (RuntimeException ex) {
unFinishExists = true;
}
try {
this.remoteTerminator.commit(xid, false);
committedExists = true;
} catch (XAException ex) {
// error: XA_HEURHAZ, XA_HEURMIX, XA_HEURCOM, XA_HEURRB, XA_RDONLY, XAER_RMERR
switch (ex.errorCode) {
case XAException.XA_HEURCOM:
committedExists = true;
break;
case XAException.XA_HEURRB:
rolledbackExists = true;
break;
case XAException.XA_HEURMIX:
committedExists = true;
rolledbackExists = true;
break;
case XAException.XA_HEURHAZ:
unFinishExists = true;
break;
case XAException.XA_RDONLY:
break;
case XAException.XAER_RMERR:
errorExists = true;
break;
default:
// should never happen
errorExists = true;
}
} catch (RuntimeException ex) {
unFinishExists = true;
}
if (committedExists && rolledbackExists) {
throw new HeuristicMixedException();
} else if (unFinishExists) {
throw new SystemException(); // hazard
} else if (errorExists) {
throw new SystemException();
} else if (rolledbackExists) {
throw new HeuristicRollbackException();
}
}
public void rollback(Xid xid)
throws HeuristicMixedException, HeuristicCommitException, IllegalStateException, SystemException {
boolean committedExists = false;
boolean rolledbackExists = false;
boolean unFinishExists = false;
boolean errorExists = false;
try {
this.nativeTerminator.rollback(xid);
rolledbackExists = true;
} catch (XAException ex) {
// error: XA_HEURHAZ, XA_HEURMIX, XA_HEURCOM, XA_HEURRB, XA_RDONLY, XAER_RMERR
switch (ex.errorCode) {
case XAException.XA_HEURCOM:
committedExists = true;
break;
case XAException.XA_HEURRB:
rolledbackExists = true;
break;
case XAException.XA_HEURMIX:
committedExists = true;
rolledbackExists = true;
break;
case XAException.XA_HEURHAZ:
unFinishExists = true;
break;
case XAException.XA_RDONLY:
break;
case XAException.XAER_RMERR:
errorExists = true;
break;
default:
// should never happen
errorExists = true;
}
} catch (RuntimeException ex) {
unFinishExists = true;
}
try {
this.remoteTerminator.rollback(xid);
rolledbackExists = true;
} catch (XAException ex) {
// error: XA_HEURHAZ, XA_HEURMIX, XA_HEURCOM, XA_HEURRB, XA_RDONLY, XAER_RMERR
switch (ex.errorCode) {
case XAException.XA_HEURCOM:
committedExists = true;
break;
case XAException.XA_HEURRB:
rolledbackExists = true;
break;
case XAException.XA_HEURMIX:
committedExists = true;
rolledbackExists = true;
break;
case XAException.XA_HEURHAZ:
unFinishExists = true;
break;
case XAException.XA_RDONLY:
break;
case XAException.XAER_RMERR:
errorExists = true;
break;
default:
// should never happen
errorExists = true;
}
} catch (RuntimeException ex) {
unFinishExists = true;
}
if (committedExists && rolledbackExists) {
throw new HeuristicMixedException();
} else if (unFinishExists) {
throw new SystemException(); // hazard
} else if (errorExists) {
throw new SystemException();
} else if (committedExists) {
throw new HeuristicCommitException();
}
}
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/bytejta/strategy/LastResourceOptimizeStrategy.java
================================================
/**
* Copyright 2014-2017 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.bytejta.strategy;
import javax.transaction.HeuristicCommitException;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.SystemException;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import org.bytesoft.bytejta.TransactionStrategy;
import org.bytesoft.transaction.CommitRequiredException;
import org.bytesoft.transaction.RollbackRequiredException;
import org.bytesoft.transaction.resource.XATerminator;
public class LastResourceOptimizeStrategy implements TransactionStrategy {
private final XATerminator terminatorOne;
private final XATerminator terminatorTwo;
public LastResourceOptimizeStrategy(XATerminator terminatorOne, XATerminator terminatorTwo) {
if (terminatorOne == null || terminatorOne.getResourceArchives().size() != 1) {
throw new IllegalStateException();
} else if (terminatorTwo == null || terminatorTwo.getResourceArchives().isEmpty()) {
throw new IllegalStateException();
}
this.terminatorOne = terminatorOne;
this.terminatorTwo = terminatorTwo;
}
public int prepare(Xid xid) throws RollbackRequiredException, CommitRequiredException {
int vote = XAResource.XA_RDONLY;
try {
vote = this.terminatorTwo.prepare(xid);
} catch (Exception ex) {
throw new RollbackRequiredException();
}
try {
this.terminatorOne.commit(xid, true);
} catch (XAException ex) {
// error: XA_HEURHAZ, XA_HEURMIX, XA_HEURCOM, XA_HEURRB, XA_RDONLY, XAER_RMERR
switch (ex.errorCode) {
case XAException.XA_HEURCOM:
throw new CommitRequiredException();
case XAException.XA_HEURRB:
throw new RollbackRequiredException();
case XAException.XA_HEURMIX:
throw new CommitRequiredException();
case XAException.XA_HEURHAZ:
throw new CommitRequiredException(); // TODO
case XAException.XA_RDONLY:
return vote;
case XAException.XAER_RMERR:
default:
throw new RollbackRequiredException();
}
} catch (RuntimeException rex) {
throw new RollbackRequiredException();
}
throw new CommitRequiredException();
}
public void commit(Xid xid, boolean onePhaseCommit)
throws HeuristicMixedException, HeuristicRollbackException, IllegalStateException, SystemException {
try {
this.terminatorTwo.commit(xid, onePhaseCommit);
} catch (XAException ex) {
// error: XA_HEURHAZ, XA_HEURMIX, XA_HEURCOM, XA_HEURRB, XA_RDONLY, XAER_RMERR
switch (ex.errorCode) {
case XAException.XA_HEURCOM:
break;
case XAException.XA_HEURRB:
throw new HeuristicRollbackException();
case XAException.XA_HEURMIX:
throw new HeuristicMixedException();
case XAException.XA_HEURHAZ:
throw new SystemException();
case XAException.XA_RDONLY:
break;
case XAException.XAER_RMERR:
throw new SystemException();
default:
// should never happen
throw new SystemException();
}
} catch (RuntimeException ex) {
throw new SystemException();
}
}
public void rollback(Xid xid)
throws HeuristicMixedException, HeuristicCommitException, IllegalStateException, SystemException {
boolean committedExists = false;
boolean rolledbackExists = false;
boolean unFinishExists = false;
boolean errorExists = false;
try {
this.terminatorOne.rollback(xid);
rolledbackExists = true;
} catch (XAException ex) {
// error: XA_HEURHAZ, XA_HEURMIX, XA_HEURCOM, XA_HEURRB, XA_RDONLY, XAER_RMERR
switch (ex.errorCode) {
case XAException.XA_HEURCOM:
committedExists = true;
break;
case XAException.XA_HEURRB:
rolledbackExists = true;
break;
case XAException.XA_HEURMIX:
committedExists = true;
rolledbackExists = true;
break;
case XAException.XA_HEURHAZ:
unFinishExists = true;
break;
case XAException.XA_RDONLY:
break;
case XAException.XAER_RMERR:
errorExists = true;
break;
default:
// should never happen
errorExists = true;
}
} catch (RuntimeException ex) {
unFinishExists = true;
}
try {
this.terminatorTwo.rollback(xid);
rolledbackExists = true;
} catch (XAException ex) {
// error: XA_HEURHAZ, XA_HEURMIX, XA_HEURCOM, XA_HEURRB, XA_RDONLY, XAER_RMERR
switch (ex.errorCode) {
case XAException.XA_HEURCOM:
committedExists = true;
break;
case XAException.XA_HEURRB:
rolledbackExists = true;
break;
case XAException.XA_HEURMIX:
committedExists = true;
rolledbackExists = true;
break;
case XAException.XA_HEURHAZ:
unFinishExists = true;
break;
case XAException.XA_RDONLY:
break;
case XAException.XAER_RMERR:
errorExists = true;
break;
default:
// should never happen
errorExists = true;
}
} catch (RuntimeException ex) {
unFinishExists = true;
}
if (committedExists && rolledbackExists) {
throw new HeuristicMixedException();
} else if (unFinishExists) {
throw new SystemException(); // hazard
} else if (errorExists) {
throw new SystemException();
} else if (committedExists) {
throw new HeuristicCommitException();
}
}
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/bytejta/strategy/SimpleTransactionStrategy.java
================================================
/**
* Copyright 2014-2017 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.bytejta.strategy;
import javax.transaction.HeuristicCommitException;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.SystemException;
import javax.transaction.xa.XAException;
import javax.transaction.xa.Xid;
import org.bytesoft.bytejta.TransactionStrategy;
import org.bytesoft.transaction.CommitRequiredException;
import org.bytesoft.transaction.RollbackRequiredException;
import org.bytesoft.transaction.resource.XATerminator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SimpleTransactionStrategy implements TransactionStrategy {
static final Logger logger = LoggerFactory.getLogger(SimpleTransactionStrategy.class);
private final XATerminator terminator;
public SimpleTransactionStrategy(XATerminator terminator) {
if (terminator == null || terminator.getResourceArchives().isEmpty()) {
throw new IllegalStateException();
}
this.terminator = terminator;
}
public int prepare(Xid xid) throws RollbackRequiredException, CommitRequiredException {
try {
return this.terminator.prepare(xid);
} catch (XAException xaex) {
throw new RollbackRequiredException();
} catch (RuntimeException xaex) {
throw new RollbackRequiredException();
}
}
public void commit(Xid xid, boolean onePhaseCommit)
throws HeuristicMixedException, HeuristicRollbackException, IllegalStateException, SystemException {
try {
this.terminator.commit(xid, onePhaseCommit);
} catch (XAException xaex) {
switch (xaex.errorCode) {
case XAException.XA_HEURCOM:
break;
case XAException.XA_HEURMIX:
throw new HeuristicMixedException();
case XAException.XA_HEURRB:
throw new HeuristicRollbackException();
default:
logger.error("Unknown state in committing transaction phase.", xaex);
throw new SystemException();
}
} catch (RuntimeException rex) {
logger.error("Unknown state in committing transaction phase.", rex);
throw new SystemException();
}
}
public void rollback(Xid xid)
throws HeuristicMixedException, HeuristicCommitException, IllegalStateException, SystemException {
try {
this.terminator.rollback(xid);
} catch (XAException xaex) {
switch (xaex.errorCode) {
case XAException.XA_HEURRB:
break;
case XAException.XA_HEURMIX:
throw new HeuristicMixedException();
case XAException.XA_HEURCOM:
throw new HeuristicCommitException();
default:
logger.error("Unknown state in rollingback transaction phase.", xaex);
throw new SystemException();
}
} catch (RuntimeException rex) {
logger.error("Unknown state in rollingback transaction phase.", rex);
throw new SystemException();
}
}
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/bytejta/strategy/VacantTransactionStrategy.java
================================================
/**
* Copyright 2014-2017 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.bytejta.strategy;
import javax.transaction.HeuristicCommitException;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.SystemException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import org.bytesoft.bytejta.TransactionStrategy;
import org.bytesoft.transaction.CommitRequiredException;
import org.bytesoft.transaction.RollbackRequiredException;
public class VacantTransactionStrategy implements TransactionStrategy {
public int prepare(Xid xid) throws RollbackRequiredException, CommitRequiredException {
return XAResource.XA_RDONLY;
}
public void commit(Xid xid, boolean onePhaseCommit)
throws HeuristicMixedException, HeuristicRollbackException, IllegalStateException, SystemException {
}
public void rollback(Xid xid)
throws HeuristicMixedException, HeuristicCommitException, IllegalStateException, SystemException {
}
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/bytejta/supports/jdbc/DataSourceHolder.java
================================================
/**
* Copyright 2014-2016 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.bytejta.supports.jdbc;
import javax.sql.DataSource;
public interface DataSourceHolder {
public DataSource getDataSource();
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/bytejta/supports/jdbc/LocalXACompatible.java
================================================
/**
* Copyright 2014-2017 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.bytejta.supports.jdbc;
public interface LocalXACompatible {
public boolean compatibleLoggingLRO();
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/bytejta/supports/jdbc/LocalXAConnection.java
================================================
/**
* Copyright 2014-2016 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.bytejta.supports.jdbc;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.sql.ConnectionEvent;
import javax.sql.ConnectionEventListener;
import javax.sql.StatementEventListener;
import javax.sql.XAConnection;
import org.bytesoft.bytejta.supports.resource.LocalXAResourceDescriptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LocalXAConnection implements XAConnection {
static final Logger logger = LoggerFactory.getLogger(LocalXAConnection.class);
private String resourceId;
private final Connection connection;
private final LocalXAResource xaResource = new LocalXAResource(this);
private boolean underlyingConCloseRequired = false;
private boolean physicalConnectionReleased = false;
private int physicalConnectionSharingCount = 0;
private final Set listeners = new HashSet();
public LocalXAConnection(Connection connection) {
this.connection = connection;
}
protected Connection getPhysicalConnection() {
return this.connection;
}
public LogicalConnection getConnection() throws SQLException {
if (this.physicalConnectionReleased) {
throw new SQLException("LocalXAConnection has already been closed!");
}
LogicalConnection logicalConnection = new LogicalConnection(this, this.connection);
this.physicalConnectionSharingCount++;
this.underlyingConCloseRequired = false;
return logicalConnection;
}
public void closeLogicalConnection() throws SQLException {
this.physicalConnectionSharingCount--;
if (this.physicalConnectionSharingCount == 0) {
this.underlyingConCloseRequired = true;
}
}
private void releaseConnection() {
if (this.physicalConnectionReleased == false) {
try {
this.connection.close();
this.fireConnectionClosed();
} catch (SQLException ex) {
logger.debug("Error occurred while closing connection!", ex);
this.fireConnectionErrorOccurred();
} catch (RuntimeException ex) {
logger.debug("Error occurred while closing connection!", ex);
this.fireConnectionErrorOccurred();
} finally {
this.physicalConnectionReleased = true;
}
}
}
public void commitLocalTransaction() throws SQLException {
try {
this.connection.commit();
} catch (SQLException ex) {
throw ex;
} catch (RuntimeException ex) {
throw new SQLException(ex);
}
}
public void rollbackLocalTransaction() throws SQLException {
try {
this.connection.rollback();
} catch (SQLException ex) {
throw ex;
} catch (RuntimeException ex) {
throw new SQLException(ex);
}
}
public void closeQuietly() {
try {
this.close();
} catch (Exception ex) {
logger.warn("Error occurred while closing physical connection.", ex);
}
}
public void close() throws SQLException {
if (this.underlyingConCloseRequired == false) {
logger.warn("Illegal state: there is at least one connection that is not closed!");
}
this.releaseConnection();
}
private void fireConnectionClosed() {
Iterator itr = this.listeners.iterator();
while (itr.hasNext()) {
ConnectionEventListener listener = itr.next();
try {
listener.connectionClosed(new ConnectionEvent(this));
} catch (Exception ex) {
logger.debug(ex.getMessage(), ex);
}
}
}
private void fireConnectionErrorOccurred() {
Iterator itr = this.listeners.iterator();
while (itr.hasNext()) {
ConnectionEventListener listener = itr.next();
try {
listener.connectionErrorOccurred(new ConnectionEvent(this));
} catch (Exception ex) {
logger.debug(ex.getMessage(), ex);
}
}
}
public void addConnectionEventListener(ConnectionEventListener paramConnectionEventListener) {
this.listeners.add(paramConnectionEventListener);
}
public void removeConnectionEventListener(ConnectionEventListener paramConnectionEventListener) {
this.listeners.remove(paramConnectionEventListener);
}
public void addStatementEventListener(StatementEventListener paramStatementEventListener) {
}
public void removeStatementEventListener(StatementEventListener paramStatementEventListener) {
}
public LocalXAResourceDescriptor getXAResource(boolean loggingRequired) throws SQLException {
LocalXAResourceDescriptor descriptor = new LocalXAResourceDescriptor();
descriptor.setIdentifier(this.resourceId);
descriptor.setDelegate(this.xaResource);
descriptor.setLoggingRequired(loggingRequired);
return descriptor;
}
public LocalXAResourceDescriptor getXAResource() throws SQLException {
return this.getXAResource(true);
}
public String getResourceId() {
return resourceId;
}
public void setResourceId(String resourceId) {
this.resourceId = resourceId;
}
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/bytejta/supports/jdbc/LocalXAResource.java
================================================
/**
* Copyright 2014-2016 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.bytejta.supports.jdbc;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import org.bytesoft.common.utils.ByteUtils;
import org.bytesoft.transaction.xa.XidFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LocalXAResource implements XAResource {
static final Logger logger = LoggerFactory.getLogger(LocalXAResource.class);
private LocalXAConnection managedConnection;
private Xid currentXid;
private Xid suspendXid;
private boolean suspendAutoCommit;
private boolean originalAutoCommit;
public LocalXAResource() {
}
public LocalXAResource(LocalXAConnection managedConnection) {
this.managedConnection = managedConnection;
}
public void recoverable(Xid xid) throws XAException {
byte[] globalTransactionId = xid.getGlobalTransactionId();
byte[] branchQualifier = xid.getBranchQualifier();
String gxid = ByteUtils.byteArrayToString(globalTransactionId);
String bxid = null;
if (branchQualifier == null || branchQualifier.length == 0) {
bxid = gxid;
} else {
bxid = ByteUtils.byteArrayToString(branchQualifier);
}
String identifier = this.getIdentifier(globalTransactionId, branchQualifier);
Connection connection = this.managedConnection.getPhysicalConnection();
PreparedStatement stmt = null;
ResultSet rs = null;
try {
StringBuilder sql = new StringBuilder();
sql.append("select xid, gxid, bxid from bytejta where xid = ? gxid = ? and bxid = ? ");
stmt = connection.prepareStatement(sql.toString());
stmt.setString(1, identifier);
stmt.setString(2, gxid);
stmt.setString(3, bxid);
rs = stmt.executeQuery();
if (rs.next() == false) {
throw new XAException(XAException.XAER_NOTA);
}
} catch (SQLException ex) {
try {
this.isTableExists(connection);
} catch (SQLException sqlEx) {
logger.warn("Error occurred while recovering local-xa-resource.", ex);
throw new XAException(XAException.XAER_RMFAIL);
} catch (RuntimeException rex) {
logger.warn("Error occurred while recovering local-xa-resource.", ex);
throw new XAException(XAException.XAER_RMFAIL);
}
throw new XAException(XAException.XAER_RMERR);
} catch (RuntimeException ex) {
logger.warn("Error occurred while recovering local-xa-resource.", ex);
throw new XAException(XAException.XAER_RMERR);
} finally {
this.closeQuietly(rs);
this.closeQuietly(stmt);
}
}
public synchronized void start(Xid xid, int flags) throws XAException {
if (xid == null) {
throw new XAException(XAException.XAER_INVAL);
} else if (flags == XAResource.TMRESUME && this.suspendXid != null) {
if (this.suspendXid.equals(xid)) {
this.suspendXid = null;
this.currentXid = xid;
this.originalAutoCommit = this.suspendAutoCommit;
this.suspendAutoCommit = true;
return;
} else {
throw new XAException(XAException.XAER_PROTO);
}
} else if (flags == XAResource.TMJOIN) {
if (this.currentXid == null) {
throw new XAException(XAException.XAER_PROTO);
}
} else if (flags != XAResource.TMNOFLAGS) {
throw new XAException(XAException.XAER_PROTO);
} else if (this.currentXid != null) {
throw new XAException(XAException.XAER_PROTO);
} else {
Connection connection = this.managedConnection.getPhysicalConnection();
try {
originalAutoCommit = connection.getAutoCommit();
} catch (Exception ignored) {
originalAutoCommit = true;
}
try {
connection.setAutoCommit(false);
} catch (Exception ex) {
XAException xae = new XAException(XAException.XAER_RMERR);
xae.initCause(ex);
throw xae;
}
this.currentXid = xid;
}
}
public synchronized void end(Xid xid, int flags) throws XAException {
if (xid == null) {
throw new XAException(XAException.XAER_INVAL);
} else if (this.currentXid == null) {
throw new XAException(XAException.XAER_PROTO);
} else if (!this.currentXid.equals(xid)) {
throw new XAException(XAException.XAER_PROTO);
} else if (flags == XAResource.TMSUSPEND) {
this.suspendXid = xid;
this.suspendAutoCommit = this.originalAutoCommit;
this.currentXid = null;
this.originalAutoCommit = true;
} else if (flags == XAResource.TMSUCCESS) {
// delay the logging operation to the commit phase.
// this.createTransactionLogIfNecessary(xid);
} else if (flags == XAResource.TMFAIL) {
logger.debug("Error occurred while ending local-xa-resource.");
} else {
throw new XAException(XAException.XAER_PROTO);
}
}
public synchronized int prepare(Xid xid) {
Connection connection = this.managedConnection.getPhysicalConnection();
try {
if (connection.isReadOnly()) {
connection.setAutoCommit(originalAutoCommit);
return XAResource.XA_RDONLY;
}
} catch (Exception ex) {
logger.debug("Error occurred while preparing local-xa-resource: {}", ex.getMessage());
}
return XAResource.XA_OK;
}
public synchronized void commit(Xid xid, boolean loggingRequired) throws XAException {
try {
if (xid == null) {
throw new XAException(XAException.XAER_INVAL);
} else if (this.currentXid == null) {
throw new XAException(XAException.XAER_PROTO);
} else if (!this.currentXid.equals(xid)) {
throw new XAException(XAException.XAER_PROTO);
}
if (loggingRequired) {
this.createTransactionLogIfNecessary(xid);
} // end-if (loggingRequired)
this.managedConnection.commitLocalTransaction();
} catch (XAException xae) {
throw xae;
} catch (Exception ex) {
XAException xae = new XAException(XAException.XAER_RMERR);
xae.initCause(ex);
throw xae;
} finally {
this.releasePhysicalConnection();
}
}
public synchronized void rollback(Xid xid) throws XAException {
try {
if (xid == null) {
throw new XAException(XAException.XAER_INVAL);
} else if (this.currentXid == null) {
throw new XAException(XAException.XAER_PROTO);
} else if (!this.currentXid.equals(xid)) {
throw new XAException(XAException.XAER_PROTO);
}
this.managedConnection.rollbackLocalTransaction();
} catch (XAException xae) {
throw xae;
} catch (Exception ex) {
XAException xae = new XAException(XAException.XAER_RMERR);
xae.initCause(ex);
throw xae;
} finally {
this.releasePhysicalConnection();
}
}
private void createTransactionLogIfNecessary(Xid xid) throws XAException {
byte[] globalTransactionId = xid.getGlobalTransactionId();
byte[] branchQualifier = xid.getBranchQualifier();
String gxid = ByteUtils.byteArrayToString(globalTransactionId);
String bxid = null;
if (branchQualifier == null || branchQualifier.length == 0) {
bxid = gxid;
} else {
bxid = ByteUtils.byteArrayToString(branchQualifier);
}
String identifier = this.getIdentifier(globalTransactionId, branchQualifier);
Connection connection = this.managedConnection.getPhysicalConnection();
PreparedStatement stmt = null;
try {
stmt = connection.prepareStatement("insert into bytejta(xid, gxid, bxid, ctime) values(?, ?, ?, ?)");
stmt.setString(1, identifier);
stmt.setString(2, gxid);
stmt.setString(3, bxid);
stmt.setLong(4, System.currentTimeMillis());
int value = stmt.executeUpdate();
if (value == 0) {
throw new IllegalStateException("The operation failed and the data was not written to the database!");
}
} catch (SQLException ex) {
boolean tableExists = false;
try {
tableExists = this.isTableExists(connection);
} catch (Exception sqlEx) {
logger.error("Error occurred while ending local-xa-resource: {}", ex.getMessage());
throw new XAException(XAException.XAER_RMFAIL);
}
if (tableExists) {
logger.error("Error occurred while ending local-xa-resource: {}", ex.getMessage());
throw new XAException(XAException.XAER_RMERR);
} else {
logger.debug("Error occurred while ending local-xa-resource: {}", ex.getMessage());
}
} catch (RuntimeException rex) {
logger.error("Error occurred while ending local-xa-resource: {}", rex.getMessage());
throw new XAException(XAException.XAER_RMERR);
} finally {
this.closeQuietly(stmt);
}
}
private void releasePhysicalConnection() {
Connection connection = this.managedConnection.getPhysicalConnection();
try {
connection.setAutoCommit(originalAutoCommit);
} catch (Exception ex) {
logger.warn("Error occurred while configuring attr 'autoCommit' of physical connection.", ex);
} finally {
// LocalXAConnection is only used for wrapping,
// once the transaction completed it can be closed immediately.
this.managedConnection.closeQuietly();
this.forgetQuietly(this.currentXid);
}
}
public boolean isSameRM(XAResource xares) {
if (this == xares) {
return true;
} else if (LocalXAResource.class.isInstance(xares) == false) {
return false;
}
LocalXAResource that = (LocalXAResource) xares;
Connection thisConn = this.managedConnection.getPhysicalConnection();
Connection thatConn = that.managedConnection.getPhysicalConnection();
return thisConn == thatConn;
}
public void forgetQuietly(Xid xid) {
try {
this.forget(xid);
} catch (Exception ex) {
logger.warn("Error occurred while forgeting local-xa-resource.", xid);
}
}
public synchronized void forget(Xid xid) throws XAException {
if (xid == null || this.currentXid == null) {
logger.warn("Error occurred while forgeting local-xa-resource: invalid xid.");
} else {
this.currentXid = null;
this.originalAutoCommit = true;
this.managedConnection = null;
}
}
public Xid[] recover(int flags) throws XAException {
return new Xid[0];
}
protected boolean isTableExists(Connection conn) throws SQLException {
String catalog = null;
try {
catalog = conn.getCatalog();
} catch (Throwable throwable) {
logger.debug("Error occurred while getting catalog of java.sql.Connection!");
}
String schema = null;
try {
schema = conn.getSchema();
} catch (Throwable throwable) {
logger.debug("Error occurred while getting schema of java.sql.Connection!");
}
ResultSet rs = null;
try {
DatabaseMetaData metadata = conn.getMetaData();
rs = metadata.getTables(catalog, schema, "bytejta", null);
return rs.next();
} finally {
this.closeQuietly(rs);
}
}
protected void closeQuietly(ResultSet closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (Exception ex) {
logger.debug("Error occurred while closing resource {}.", closeable);
}
}
}
protected void closeQuietly(Statement closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (Exception ex) {
logger.debug("Error occurred while closing resource {}.", closeable);
}
}
}
protected void closeQuietly(Connection closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (Exception ex) {
logger.debug("Error occurred while closing resource {}.", closeable);
}
}
}
protected String getIdentifier(byte[] globalByteArray, byte[] branchByteArray) {
if (branchByteArray == null || branchByteArray.length != XidFactory.BRANCH_QUALIFIER_LENGTH) {
logger.warn("Invalid branchByteArray: the length of branchQulifier not equals to 16!");
return ByteUtils.byteArrayToString(globalByteArray);
}
byte[] prefixByteArray = new byte[5];
System.arraycopy(branchByteArray, 0, prefixByteArray, 0, 5);
byte[] gvalueByteArray = new byte[4];
System.arraycopy(globalByteArray, 0, gvalueByteArray, 0, 4);
int global = ByteUtils.byteArrayToInt(gvalueByteArray);
int gday = (global << 8) >>> 27;
int ghour = (global << 13) >>> 27;
int gminute = (global << 18) >>> 26;
int gsecond = (global << 24) >>> 26;
int gmillis = ((global << 30) >>> 22) | (globalByteArray[4] - Byte.MIN_VALUE);
int datime = 0;
datime = datime | (gday << 27);
datime = datime | (ghour << 22);
datime = datime | (gminute << 16);
datime = datime | (gsecond << 10);
datime = datime | gmillis;
byte[] datimeByteArray = ByteUtils.intToByteArray(datime);
byte[] randomByteArray = new byte[4];
System.arraycopy(branchByteArray, 12, randomByteArray, 0, 4);
byte[] resultByteArray = new byte[16];
System.arraycopy(prefixByteArray, 0, resultByteArray, 0, 5);
System.arraycopy(datimeByteArray, 1, resultByteArray, 5, 3);
System.arraycopy(branchByteArray, 5, resultByteArray, 8, 6);
System.arraycopy(randomByteArray, 2, resultByteArray, 14, 2);
return ByteUtils.byteArrayToString(resultByteArray);
}
public int getTransactionTimeout() {
return 0;
}
public boolean setTransactionTimeout(int transactionTimeout) {
return false;
}
public LocalXAConnection getManagedConnection() {
return managedConnection;
}
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/bytejta/supports/jdbc/LogicalConnection.java
================================================
/**
* Copyright 2014-2016 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.bytejta.supports.jdbc;
import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogicalConnection implements Connection {
static final Logger logger = LoggerFactory.getLogger(LogicalConnection.class);
private boolean connectionClosed;
private final LocalXAConnection managedConnection;
private final Connection delegateConnection;
public LogicalConnection(LocalXAConnection managedConnection, Connection connection) {
this.managedConnection = managedConnection;
this.delegateConnection = connection;
}
public T unwrap(Class iface) throws SQLException {
this.validateConnectionStatus();
return delegateConnection.unwrap(iface);
}
public boolean isWrapperFor(Class> iface) throws SQLException {
this.validateConnectionStatus();
return delegateConnection.isWrapperFor(iface);
}
public Statement createStatement() throws SQLException {
this.validateConnectionStatus();
return delegateConnection.createStatement();
}
public PreparedStatement prepareStatement(String sql) throws SQLException {
this.validateConnectionStatus();
return delegateConnection.prepareStatement(sql);
}
public CallableStatement prepareCall(String sql) throws SQLException {
this.validateConnectionStatus();
return delegateConnection.prepareCall(sql);
}
public String nativeSQL(String sql) throws SQLException {
this.validateConnectionStatus();
return delegateConnection.nativeSQL(sql);
}
public void setAutoCommit(boolean autoCommit) throws SQLException {
throw new SQLException("Illegal operation!");
}
public boolean getAutoCommit() throws SQLException {
this.validateConnectionStatus();
return delegateConnection.getAutoCommit();
}
public void commit() throws SQLException {
this.validateConnectionStatus();
throw new SQLException("Illegal operation!");
}
public void rollback() throws SQLException {
this.validateConnectionStatus();
throw new SQLException("Illegal operation!");
}
public synchronized void close() throws SQLException {
if (this.connectionClosed) {
logger.debug("Current connection has already been closed.");
} else {
this.connectionClosed = true;
managedConnection.closeLogicalConnection();
}
}
public boolean isClosed() throws SQLException {
return this.connectionClosed;
}
public DatabaseMetaData getMetaData() throws SQLException {
this.validateConnectionStatus();
return delegateConnection.getMetaData();
}
public void setReadOnly(boolean readOnly) throws SQLException {
this.validateConnectionStatus();
delegateConnection.setReadOnly(readOnly);
}
public boolean isReadOnly() throws SQLException {
this.validateConnectionStatus();
return delegateConnection.isReadOnly();
}
public void setCatalog(String catalog) throws SQLException {
this.validateConnectionStatus();
delegateConnection.setCatalog(catalog);
}
public String getCatalog() throws SQLException {
this.validateConnectionStatus();
return delegateConnection.getCatalog();
}
public void setTransactionIsolation(int level) throws SQLException {
this.validateConnectionStatus();
delegateConnection.setTransactionIsolation(level);
}
public int getTransactionIsolation() throws SQLException {
this.validateConnectionStatus();
return delegateConnection.getTransactionIsolation();
}
public SQLWarning getWarnings() throws SQLException {
this.validateConnectionStatus();
return delegateConnection.getWarnings();
}
public void clearWarnings() throws SQLException {
this.validateConnectionStatus();
delegateConnection.clearWarnings();
}
public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
this.validateConnectionStatus();
return delegateConnection.createStatement(resultSetType, resultSetConcurrency);
}
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
this.validateConnectionStatus();
return delegateConnection.prepareStatement(sql, resultSetType, resultSetConcurrency);
}
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
this.validateConnectionStatus();
return delegateConnection.prepareCall(sql, resultSetType, resultSetConcurrency);
}
public Map> getTypeMap() throws SQLException {
this.validateConnectionStatus();
return delegateConnection.getTypeMap();
}
public void setTypeMap(Map> map) throws SQLException {
this.validateConnectionStatus();
delegateConnection.setTypeMap(map);
}
public void setHoldability(int holdability) throws SQLException {
this.validateConnectionStatus();
delegateConnection.setHoldability(holdability);
}
public int getHoldability() throws SQLException {
this.validateConnectionStatus();
return delegateConnection.getHoldability();
}
public Savepoint setSavepoint() throws SQLException {
this.validateConnectionStatus();
return delegateConnection.setSavepoint();
}
public Savepoint setSavepoint(String name) throws SQLException {
this.validateConnectionStatus();
return delegateConnection.setSavepoint(name);
}
public void rollback(Savepoint savepoint) throws SQLException {
this.validateConnectionStatus();
delegateConnection.rollback(savepoint);
}
public void releaseSavepoint(Savepoint savepoint) throws SQLException {
this.validateConnectionStatus();
delegateConnection.releaseSavepoint(savepoint);
}
public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability)
throws SQLException {
this.validateConnectionStatus();
return delegateConnection.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);
}
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability)
throws SQLException {
this.validateConnectionStatus();
return delegateConnection.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
}
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability)
throws SQLException {
this.validateConnectionStatus();
return delegateConnection.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
}
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
this.validateConnectionStatus();
return delegateConnection.prepareStatement(sql, autoGeneratedKeys);
}
public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
this.validateConnectionStatus();
return delegateConnection.prepareStatement(sql, columnIndexes);
}
public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
this.validateConnectionStatus();
return delegateConnection.prepareStatement(sql, columnNames);
}
public Clob createClob() throws SQLException {
this.validateConnectionStatus();
return delegateConnection.createClob();
}
public Blob createBlob() throws SQLException {
this.validateConnectionStatus();
return delegateConnection.createBlob();
}
public NClob createNClob() throws SQLException {
this.validateConnectionStatus();
return delegateConnection.createNClob();
}
public SQLXML createSQLXML() throws SQLException {
this.validateConnectionStatus();
return delegateConnection.createSQLXML();
}
public boolean isValid(int timeout) throws SQLException {
return delegateConnection.isValid(timeout);
}
public void setClientInfo(String name, String value) throws SQLClientInfoException {
try {
this.validateConnectionStatus();
} catch (SQLException ex) {
throw new SQLClientInfoException(null, ex);
}
delegateConnection.setClientInfo(name, value);
}
public void setClientInfo(Properties properties) throws SQLClientInfoException {
try {
this.validateConnectionStatus();
} catch (SQLException ex) {
throw new SQLClientInfoException(null, ex);
}
delegateConnection.setClientInfo(properties);
}
public String getClientInfo(String name) throws SQLException {
this.validateConnectionStatus();
return delegateConnection.getClientInfo(name);
}
public Properties getClientInfo() throws SQLException {
this.validateConnectionStatus();
return delegateConnection.getClientInfo();
}
public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
this.validateConnectionStatus();
return delegateConnection.createArrayOf(typeName, elements);
}
public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
this.validateConnectionStatus();
return delegateConnection.createStruct(typeName, attributes);
}
public void setSchema(String schema) throws SQLException {
this.validateConnectionStatus();
delegateConnection.setSchema(schema);
}
public String getSchema() throws SQLException {
this.validateConnectionStatus();
return delegateConnection.getSchema();
}
public void abort(Executor executor) throws SQLException {
this.validateConnectionStatus();
delegateConnection.abort(executor);
}
public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
this.validateConnectionStatus();
delegateConnection.setNetworkTimeout(executor, milliseconds);
}
public int getNetworkTimeout() throws SQLException {
this.validateConnectionStatus();
return delegateConnection.getNetworkTimeout();
}
private void validateConnectionStatus() throws SQLException {
if (this.connectionClosed) {
throw new SQLException("Connection is closed");
}
}
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/bytejta/supports/jdbc/RecoveredResource.java
================================================
/**
* Copyright 2014-2016 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.bytejta.supports.jdbc;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.sql.DataSource;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import org.apache.commons.lang3.StringUtils;
import org.bytesoft.common.utils.ByteUtils;
import org.bytesoft.transaction.xa.TransactionXid;
import org.bytesoft.transaction.xa.XidFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RecoveredResource extends LocalXAResource implements XAResource {
static final Logger logger = LoggerFactory.getLogger(RecoveredResource.class);
private DataSource dataSource;
public void recoverable(Xid xid) throws XAException {
byte[] globalTransactionId = xid.getGlobalTransactionId();
byte[] branchQualifier = xid.getBranchQualifier();
String identifier = this.getIdentifier(globalTransactionId, branchQualifier);
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
conn = this.dataSource.getConnection();
stmt = conn.prepareStatement("select gxid, bxid from bytejta where xid = ?");
stmt.setString(1, identifier);
rs = stmt.executeQuery();
if (rs.next() == false) {
throw new XAException(XAException.XAER_NOTA);
}
} catch (SQLException ex) {
try {
this.isTableExists(conn);
} catch (SQLException sqlEx) {
logger.warn("Error occurred while recovering local-xa-resource.", ex);
throw new XAException(XAException.XAER_RMFAIL);
} catch (RuntimeException rex) {
logger.warn("Error occurred while recovering local-xa-resource.", ex);
throw new XAException(XAException.XAER_RMFAIL);
}
throw new XAException(XAException.XAER_RMERR);
} catch (RuntimeException ex) {
logger.warn("Error occurred while recovering local-xa-resource.", ex);
throw new XAException(XAException.XAER_RMERR);
} finally {
this.closeQuietly(rs);
this.closeQuietly(stmt);
this.closeQuietly(conn);
}
}
public Xid[] recover(int flags) throws XAException {
List xidList = new ArrayList();
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
conn = this.dataSource.getConnection();
stmt = conn.prepareStatement("select gxid, bxid from bytejta");
rs = stmt.executeQuery();
while (rs.next()) {
String gxid = rs.getString(1);
String bxid = rs.getString(2);
byte[] globalTransactionId = ByteUtils.stringToByteArray(gxid);
byte[] branchQualifier = ByteUtils.stringToByteArray(bxid);
TransactionXid xid = null;
if (StringUtils.equals(gxid, bxid)) {
xid = new TransactionXid(XidFactory.JTA_FORMAT_ID, globalTransactionId);
} else {
xid = new TransactionXid(XidFactory.JTA_FORMAT_ID, globalTransactionId, branchQualifier);
}
xidList.add(xid);
}
} catch (Exception ex) {
boolean tableExists = false;
try {
tableExists = this.isTableExists(conn);
} catch (Exception sqlEx) {
logger.warn("Error occurred while recovering local-xa-resource.", ex);
throw new XAException(XAException.XAER_RMFAIL);
}
if (tableExists) {
throw new XAException(XAException.XAER_RMERR);
}
} finally {
this.closeQuietly(rs);
this.closeQuietly(stmt);
this.closeQuietly(conn);
}
Xid[] xidArray = new Xid[xidList.size()];
xidList.toArray(xidArray);
return xidArray;
}
public void forgetQuietly(Xid xid) {
try {
this.forget(xid);
} catch (XAException ex) {
logger.warn("Error occurred while forgeting local-xa-resource.", xid);
}
}
public synchronized void forget(Xid[] xids) throws XAException {
if (xids == null || xids.length == 0) {
return;
}
String[] xidArray = new String[xids.length];
for (int i = 0; i < xids.length; i++) {
Xid xid = xids[i];
byte[] globalTransactionId = xid.getGlobalTransactionId();
byte[] branchQualifier = xid.getBranchQualifier();
xidArray[i] = this.getIdentifier(globalTransactionId, branchQualifier);
}
Connection conn = null;
PreparedStatement stmt = null;
Boolean autoCommit = null;
try {
conn = this.dataSource.getConnection();
autoCommit = conn.getAutoCommit();
conn.setAutoCommit(false);
stmt = conn.prepareStatement("delete from bytejta where xid = ?");
for (int i = 0; i < xids.length; i++) {
stmt.setString(1, xidArray[i]);
stmt.addBatch();
}
stmt.executeBatch();
conn.commit();
} catch (Exception ex) {
try {
conn.rollback();
} catch (Exception sqlEx) {
logger.error("Error occurred while rolling back local resources.", sqlEx);
}
boolean tableExists = false;
try {
tableExists = this.isTableExists(conn);
} catch (Exception sqlEx) {
logger.warn("Error occurred while forgeting local resources.", ex);
throw new XAException(XAException.XAER_RMFAIL);
}
if (tableExists) {
logger.error("Error occurred while forgetting resources.", ex);
throw new XAException(XAException.XAER_RMERR);
}
} finally {
this.setAutoCommitIfNecessary(conn, autoCommit);
this.closeQuietly(stmt);
this.closeQuietly(conn);
}
}
public synchronized void forget(Xid xid) throws XAException {
if (xid == null) {
logger.warn("Error occurred while forgeting local-xa-resource: invalid xid.");
return;
}
byte[] globalTransactionId = xid.getGlobalTransactionId();
byte[] branchQualifier = xid.getBranchQualifier();
String identifier = this.getIdentifier(globalTransactionId, branchQualifier);
Connection conn = null;
PreparedStatement stmt = null;
Boolean autoCommit = null;
try {
conn = this.dataSource.getConnection();
autoCommit = conn.getAutoCommit();
conn.setAutoCommit(false);
stmt = conn.prepareStatement("delete from bytejta where xid = ?");
stmt.setString(1, identifier);
stmt.executeUpdate();
conn.commit();
} catch (Exception ex) {
try {
conn.rollback();
} catch (Exception sqlEx) {
logger.error("Error occurred while rolling back local resources.", sqlEx);
}
boolean tableExists = false;
try {
tableExists = this.isTableExists(conn);
} catch (Exception sqlEx) {
logger.warn("Error occurred while forgeting local-xa-resource.", ex);
throw new XAException(XAException.XAER_RMFAIL);
}
if (tableExists) {
logger.warn("Error occurred while forgeting local-xa-resource.", ex);
throw new XAException(XAException.XAER_RMERR);
}
} finally {
this.setAutoCommitIfNecessary(conn, autoCommit);
this.closeQuietly(stmt);
this.closeQuietly(conn);
}
}
private void setAutoCommitIfNecessary(Connection conn, Boolean autoCommit) {
if (autoCommit != null) {
try {
conn.setAutoCommit(autoCommit);
} catch (SQLException sqlEx) {
logger.error("Error occurred while configuring attribute 'autoCommit'.", sqlEx);
}
} // end-if (autoCommit != null)
}
public DataSource getDataSource() {
return dataSource;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/bytejta/supports/resource/CommonResourceDescriptor.java
================================================
/**
* Copyright 2014-2016 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.bytejta.supports.resource;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import org.bytesoft.transaction.supports.resource.XAResourceDescriptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CommonResourceDescriptor implements XAResourceDescriptor {
static Logger logger = LoggerFactory.getLogger(CommonResourceDescriptor.class);
private XAResource delegate;
private String identifier;
private transient Xid recoverXid;
private transient Object managed;
// private transient boolean recved;
public boolean isTransactionCommitted(Xid xid) throws IllegalStateException {
throw new IllegalStateException();
}
public String toString() {
return String.format("common-resource[id= %s]", this.identifier);
}
public void setTransactionTimeoutQuietly(int timeout) {
try {
this.delegate.setTransactionTimeout(timeout);
} catch (Exception ex) {
// ignore
}
}
public void commit(Xid arg0, boolean arg1) throws XAException {
delegate.commit(arg0, arg1);
}
public void end(Xid arg0, int arg1) throws XAException {
delegate.end(arg0, arg1);
}
public void forget(Xid arg0) throws XAException {
try {
delegate.forget(arg0);
} finally {
this.closeIfNecessary();
}
}
private void closeIfNecessary() {
if (this.recoverXid != null && this.managed != null) {
if (javax.sql.XAConnection.class.isInstance(this.managed)) {
this.closeQuietly((javax.sql.XAConnection) this.managed);
} else if (javax.jms.XAConnection.class.isInstance(this.managed)) {
this.closeQuietly((javax.jms.XAConnection) this.managed);
} else if (javax.resource.spi.ManagedConnection.class.isInstance(this.managed)) {
this.closeQuietly((javax.resource.spi.ManagedConnection) this.managed);
}
}
}
private void closeQuietly(javax.jms.XAConnection closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (Exception ex) {
logger.debug(ex.getMessage());
}
}
}
private void closeQuietly(javax.sql.XAConnection closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (Exception ex) {
logger.debug(ex.getMessage());
}
}
}
private void closeQuietly(javax.resource.spi.ManagedConnection closeable) {
if (closeable != null) {
try {
closeable.cleanup();
} catch (Exception ex) {
logger.debug(ex.getMessage());
}
try {
closeable.destroy();
} catch (Exception ex) {
logger.debug(ex.getMessage());
}
}
}
public int getTransactionTimeout() throws XAException {
return delegate.getTransactionTimeout();
}
public boolean isSameRM(XAResource arg0) throws XAException {
return delegate.isSameRM(arg0);
}
public int prepare(Xid arg0) throws XAException {
return delegate.prepare(arg0);
}
public Xid[] recover(int arg0) throws XAException {
Xid[] xidArray = delegate.recover(arg0);
// for (int i = 0; this.recoverXid != null && i < xidArray.length; i++) {
// Xid xid = xidArray[i];
// boolean formatIdEquals = xid.getFormatId() == this.recoverXid.getFormatId();
// boolean globalTransactionIdEquals = Arrays.equals(xid.getGlobalTransactionId(),
// this.recoverXid.getGlobalTransactionId());
// boolean branchQualifierEquals = Arrays.equals(xid.getBranchQualifier(), this.recoverXid.getBranchQualifier());
// if (formatIdEquals && globalTransactionIdEquals && branchQualifierEquals) {
// this.recved = true;
// }
// }
return xidArray;
}
public void rollback(Xid arg0) throws XAException {
delegate.rollback(arg0);
}
public boolean setTransactionTimeout(int arg0) throws XAException {
return delegate.setTransactionTimeout(arg0);
}
public void start(Xid arg0, int arg1) throws XAException {
delegate.start(arg0, arg1);
}
public XAResource getDelegate() {
return delegate;
}
public void setDelegate(XAResource delegate) {
this.delegate = delegate;
}
public String getIdentifier() {
return identifier;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
public Xid getRecoverXid() {
return recoverXid;
}
public void setRecoverXid(Xid recoverXid) {
this.recoverXid = recoverXid;
}
public Object getManaged() {
return managed;
}
public void setManaged(Object managed) {
this.managed = managed;
}
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/bytejta/supports/resource/LocalXAResourceDescriptor.java
================================================
/**
* Copyright 2014-2016 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.bytejta.supports.resource;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import org.apache.commons.lang3.StringUtils;
import org.bytesoft.bytejta.supports.jdbc.LocalXAResource;
import org.bytesoft.bytejta.supports.jdbc.RecoveredResource;
import org.bytesoft.transaction.supports.resource.XAResourceDescriptor;
public class LocalXAResourceDescriptor implements XAResourceDescriptor {
private String identifier;
private LocalXAResource delegate;
private boolean loggingRequired;
public boolean isTransactionCommitted(Xid xid) throws IllegalStateException {
try {
if (RecoveredResource.class.isInstance(this.delegate)) {
((RecoveredResource) this.delegate).recoverable(xid);
} else {
((LocalXAResource) this.delegate).recoverable(xid);
}
return true;
} catch (XAException ex) {
switch (ex.errorCode) {
case XAException.XAER_NOTA:
return false;
default:
throw new IllegalStateException(ex);
}
}
}
public String toString() {
return String.format("local-xa-resource[%s]", this.identifier);
}
public void setTransactionTimeoutQuietly(int timeout) {
try {
this.delegate.setTransactionTimeout(timeout);
} catch (Exception ex) {
return;
}
}
public void commit(Xid arg0, boolean arg1) throws XAException {
if (this.delegate == null) {
return;
}
delegate.commit(arg0, this.loggingRequired); // onePhaseCommit is unnecessary.
}
public void end(Xid arg0, int arg1) throws XAException {
if (this.delegate == null) {
return;
}
delegate.end(arg0, arg1);
}
public void forget(Xid arg0) throws XAException {
if (this.delegate == null) {
return;
}
delegate.forget(arg0);
}
public int getTransactionTimeout() throws XAException {
if (this.delegate == null) {
return 0;
}
return delegate.getTransactionTimeout();
}
public boolean isSameRM(XAResource xares) throws XAException {
if (this.delegate == null) {
return false;
}
if (LocalXAResourceDescriptor.class.isInstance(xares)) {
LocalXAResourceDescriptor that = (LocalXAResourceDescriptor) xares;
boolean identifierEquals = StringUtils.equals(this.identifier, that.identifier);
boolean xaResourceEquals = this.delegate.isSameRM(that.delegate); // this.delegate != null
return identifierEquals && xaResourceEquals;
} else {
return delegate.isSameRM(xares);
}
}
public int prepare(Xid arg0) throws XAException {
if (this.delegate == null) {
return XAResource.XA_RDONLY;
}
return delegate.prepare(arg0);
}
public Xid[] recover(int arg0) throws XAException {
if (this.delegate == null) {
return new Xid[0];
}
return delegate.recover(arg0);
}
public void rollback(Xid arg0) throws XAException {
if (this.delegate == null) {
return;
}
delegate.rollback(arg0);
}
public boolean setTransactionTimeout(int arg0) throws XAException {
if (this.delegate == null) {
return false;
}
return delegate.setTransactionTimeout(arg0);
}
public void start(Xid arg0, int arg1) throws XAException {
if (this.delegate == null) {
return;
}
delegate.start(arg0, arg1);
}
public boolean isLoggingRequired() {
return loggingRequired;
}
public void setLoggingRequired(boolean loggingRequired) {
this.loggingRequired = loggingRequired;
}
public String getIdentifier() {
return identifier;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
public XAResource getDelegate() {
return delegate;
}
public void setDelegate(LocalXAResource delegate) {
this.delegate = delegate;
}
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/bytejta/supports/resource/RemoteResourceDescriptor.java
================================================
/**
* Copyright 2014-2016 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.bytejta.supports.resource;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import org.apache.commons.lang3.StringUtils;
import org.bytesoft.common.utils.CommonUtils;
import org.bytesoft.transaction.remote.RemoteAddr;
import org.bytesoft.transaction.remote.RemoteCoordinator;
import org.bytesoft.transaction.remote.RemoteNode;
import org.bytesoft.transaction.remote.RemoteSvc;
import org.bytesoft.transaction.supports.resource.XAResourceDescriptor;
public class RemoteResourceDescriptor implements XAResourceDescriptor {
public static final int X_SAME_CLUSTER = 501;
private RemoteCoordinator delegate;
private String identifier;
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
public String getIdentifier() {
if (StringUtils.isNotBlank(this.identifier)) {
return this.identifier;
} else if (this.delegate == null) {
return null;
}
this.identifier = this.delegate.getIdentifier();
return this.identifier;
}
public RemoteAddr getRemoteAddr() {
return this.delegate == null ? null : this.delegate.getRemoteAddr();
}
public RemoteNode getRemoteNode() {
return this.delegate == null ? null : this.delegate.getRemoteNode();
}
public RemoteSvc getRemoteSvc() {
return this.delegate == null ? null : CommonUtils.getRemoteSvc(this.delegate.getIdentifier());
}
public boolean isTransactionCommitted(Xid xid) throws IllegalStateException {
throw new IllegalStateException();
}
public String toString() {
return String.valueOf(this.delegate);
}
public void setTransactionTimeoutQuietly(int timeout) {
}
public void commit(Xid arg0, boolean arg1) throws XAException {
delegate.commit(arg0, arg1);
}
public void end(Xid arg0, int arg1) throws XAException {
// delegate.end(arg0, arg1);
}
public void forget(Xid arg0) throws XAException {
delegate.forget(arg0);
}
public int getTransactionTimeout() throws XAException {
throw new XAException(XAException.XAER_RMERR);
}
public boolean isSameRM(XAResource xares) throws XAException {
if (xares == null) {
return false;
} else if (RemoteResourceDescriptor.class.isInstance(xares) == false) {
return false;
}
RemoteResourceDescriptor that = (RemoteResourceDescriptor) xares;
String thisKey = this.getIdentifier();
String thatKey = that.getIdentifier();
if (StringUtils.equalsIgnoreCase(thisKey, thatKey)) {
return true;
}
if (CommonUtils.applicationEquals(thisKey, thatKey)) {
throw new XAException(X_SAME_CLUSTER);
}
return false;
}
public int prepare(Xid arg0) throws XAException {
return delegate.prepare(arg0);
}
public Xid[] recover(int arg0) throws XAException {
return delegate.recover(arg0);
}
public void rollback(Xid arg0) throws XAException {
delegate.rollback(arg0);
}
public boolean setTransactionTimeout(int arg0) throws XAException {
return true;
}
public void start(Xid arg0, int arg1) throws XAException {
delegate.start(arg0, arg1);
}
public RemoteCoordinator getDelegate() {
return delegate;
}
public void setDelegate(RemoteCoordinator delegate) {
this.delegate = delegate;
}
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/bytejta/supports/resource/UnidentifiedResourceDescriptor.java
================================================
/**
* Copyright 2014-2016 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.bytejta.supports.resource;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import org.bytesoft.transaction.supports.resource.XAResourceDescriptor;
public class UnidentifiedResourceDescriptor implements XAResourceDescriptor {
private String identifier;
private XAResource delegate;
public boolean isTransactionCommitted(Xid xid) throws IllegalStateException {
throw new IllegalStateException();
}
public String toString() {
return String.format("unknown-resource[%s]", this.delegate);
}
public void setTransactionTimeoutQuietly(int timeout) {
try {
this.delegate.setTransactionTimeout(timeout);
} catch (Exception ex) {
return;
}
}
public void commit(Xid arg0, boolean arg1) throws XAException {
if (this.delegate == null) {
return;
}
delegate.commit(arg0, arg1);
}
public void end(Xid arg0, int arg1) throws XAException {
if (this.delegate == null) {
return;
}
delegate.end(arg0, arg1);
}
public void forget(Xid arg0) throws XAException {
if (this.delegate == null) {
return;
}
delegate.forget(arg0);
}
public int getTransactionTimeout() throws XAException {
if (this.delegate == null) {
return 0;
}
return delegate.getTransactionTimeout();
}
public boolean isSameRM(XAResource arg0) throws XAException {
if (this.delegate == null) {
return false;
}
return delegate.isSameRM(arg0);
}
public int prepare(Xid arg0) throws XAException {
if (this.delegate == null) {
return XAResource.XA_RDONLY;
}
return delegate.prepare(arg0);
}
public Xid[] recover(int arg0) throws XAException {
if (this.delegate == null) {
return new Xid[0];
}
return delegate.recover(arg0);
}
public void rollback(Xid arg0) throws XAException {
if (this.delegate == null) {
return;
}
delegate.rollback(arg0);
}
public boolean setTransactionTimeout(int arg0) throws XAException {
if (this.delegate == null) {
return false;
}
return delegate.setTransactionTimeout(arg0);
}
public void start(Xid arg0, int arg1) throws XAException {
if (this.delegate == null) {
return;
}
delegate.start(arg0, arg1);
}
public String getIdentifier() {
return identifier;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
public XAResource getDelegate() {
return delegate;
}
public void setDelegate(XAResource delegate) {
this.delegate = delegate;
}
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/bytejta/work/TransactionWork.java
================================================
/**
* Copyright 2014-2016 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.bytejta.work;
import javax.resource.spi.work.Work;
import org.bytesoft.transaction.TransactionBeanFactory;
import org.bytesoft.transaction.TransactionRecovery;
import org.bytesoft.transaction.aware.TransactionBeanFactoryAware;
import org.bytesoft.transaction.supports.TransactionTimer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TransactionWork implements Work, TransactionBeanFactoryAware {
static final Logger logger = LoggerFactory.getLogger(TransactionWork.class);
@javax.inject.Inject
private TransactionBeanFactory beanFactory;
static final long SECOND_MILLIS = 1000L;
private long stopTimeMillis = -1;
private long delayOfStoping = SECOND_MILLIS * 15;
private long recoveryInterval = SECOND_MILLIS * 60;
public void run() {
TransactionTimer transactionTimer = beanFactory.getTransactionTimer();
TransactionRecovery transactionRecovery = beanFactory.getTransactionRecovery();
try {
transactionRecovery.startRecovery();
transactionRecovery.timingRecover();
} catch (SecurityException rex) {
logger.debug("Only the master node can perform the recovery operation!");
} catch (RuntimeException rex) {
logger.error("TransactionRecovery init failed!", rex);
}
long nextExecutionTime = 0;
long nextRecoveryTime = System.currentTimeMillis() + this.recoveryInterval;
while (this.currentActive()) {
long current = System.currentTimeMillis();
if (current >= nextExecutionTime) {
nextExecutionTime = current + SECOND_MILLIS;
try {
transactionTimer.timingExecution();
} catch (RuntimeException rex) {
logger.error(rex.getMessage(), rex);
}
}
if (current >= nextRecoveryTime) {
nextRecoveryTime = current + this.recoveryInterval;
this.fireGlobalRecovery();
this.fireBranchRecovery();
}
this.waitForMillis(100L);
} // end-while (this.currentActive())
}
private void fireGlobalRecovery() {
TransactionRecovery transactionRecovery = beanFactory.getTransactionRecovery();
try {
transactionRecovery.timingRecover();
} catch (SecurityException rex) {
logger.debug("Only the master node can perform the global recovery operation!");
} catch (RuntimeException rex) {
logger.error(rex.getMessage(), rex);
}
}
private void fireBranchRecovery() {
TransactionRecovery transactionRecovery = beanFactory.getTransactionRecovery();
try {
transactionRecovery.branchRecover();
} catch (SecurityException rex) {
logger.debug("Only the branch node can perform the branch recovery operation!");
} catch (RuntimeException rex) {
logger.error(rex.getMessage(), rex);
}
}
private void waitForMillis(long millis) {
try {
Thread.sleep(millis);
} catch (Exception ignore) {
// ignore
}
}
public void release() {
this.stopTimeMillis = System.currentTimeMillis() + this.delayOfStoping;
}
protected boolean currentActive() {
return this.stopTimeMillis <= 0 || System.currentTimeMillis() < this.stopTimeMillis;
}
public long getDelayOfStoping() {
return delayOfStoping;
}
public void setDelayOfStoping(long delayOfStoping) {
this.delayOfStoping = delayOfStoping;
}
public long getRecoveryInterval() {
return recoveryInterval;
}
public void setRecoveryInterval(long recoveryInterval) {
this.recoveryInterval = recoveryInterval;
}
public TransactionBeanFactory getBeanFactory() {
return this.beanFactory;
}
public void setBeanFactory(TransactionBeanFactory tbf) {
this.beanFactory = tbf;
}
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/bytejta/xa/XidFactoryImpl.java
================================================
/**
* Copyright 2014-2016 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.bytejta.xa;
import java.net.NetworkInterface;
import java.util.Calendar;
import java.util.Enumeration;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
import org.bytesoft.common.utils.ByteUtils;
import org.bytesoft.transaction.xa.TransactionXid;
import org.bytesoft.transaction.xa.XidFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class XidFactoryImpl implements XidFactory {
static final Logger logger = LoggerFactory.getLogger(XidFactoryImpl.class);
static final int SIZE_OF_MAC = 6;
static final Random random = new Random();
static final byte[] hardwareAddress = new byte[SIZE_OF_MAC];
static final AtomicInteger atomic = new AtomicInteger();
static {
byte[] sourceByteArray = getHardwareAddress();
System.arraycopy(sourceByteArray, 0, hardwareAddress, 0, SIZE_OF_MAC);
}
private static byte[] getHardwareAddress() {
Enumeration enumeration = null;
try {
enumeration = NetworkInterface.getNetworkInterfaces();
} catch (Exception ex) {
logger.debug(ex.getMessage(), ex);
}
byte[] byteArray = null;
while (byteArray == null && enumeration != null && enumeration.hasMoreElements()) {
NetworkInterface element = enumeration.nextElement();
try {
if (element.isUp() == false) {
continue;
} else if (element.isPointToPoint() || element.isVirtual()) {
continue;
} else if (element.isLoopback()) {
continue;
}
byte[] hardwareAddr = element.getHardwareAddress();
if (hardwareAddr == null || hardwareAddr.length != SIZE_OF_MAC) {
continue;
}
byteArray = new byte[SIZE_OF_MAC];
System.arraycopy(hardwareAddr, 0, byteArray, 0, SIZE_OF_MAC);
} catch (Exception rex) {
logger.debug(rex.getMessage(), rex);
}
}
return byteArray != null ? byteArray : new byte[SIZE_OF_MAC];
}
public TransactionXid createGlobalXid() {
byte[] unique = this.generateUniqueKey();
if (unique == null || unique.length != GLOBAL_TRANSACTION_LENGTH) {
throw new IllegalStateException("The length of globalTransactionId not equals to 16.");
}
byte[] global = new byte[GLOBAL_TRANSACTION_LENGTH];
System.arraycopy(unique, 0, global, 0, global.length);
return new TransactionXid(XidFactory.JTA_FORMAT_ID, global);
}
public TransactionXid createGlobalXid(byte[] globalTransactionId) {
if (globalTransactionId == null) {
throw new IllegalArgumentException("The globalTransactionId cannot be null.");
} else if (globalTransactionId.length > TransactionXid.MAXGTRIDSIZE) {
throw new IllegalArgumentException("The length of globalTransactionId cannot exceed 64 bytes.");
}
byte[] global = new byte[globalTransactionId.length];
System.arraycopy(globalTransactionId, 0, global, 0, global.length);
return new TransactionXid(XidFactory.JTA_FORMAT_ID, global);
}
public TransactionXid createBranchXid(TransactionXid globalXid) {
if (globalXid == null) {
throw new IllegalArgumentException("Xid cannot be null.");
} else if (globalXid.getGlobalTransactionId() == null) {
throw new IllegalArgumentException("The globalTransactionId cannot be null.");
} else if (globalXid.getGlobalTransactionId().length > TransactionXid.MAXGTRIDSIZE) {
throw new IllegalArgumentException("The length of globalTransactionId cannot exceed 64 bytes.");
}
byte[] global = new byte[globalXid.getGlobalTransactionId().length];
System.arraycopy(globalXid.getGlobalTransactionId(), 0, global, 0, global.length);
byte[] unique = this.generateUniqueKey();
if (unique == null || unique.length != BRANCH_QUALIFIER_LENGTH) {
throw new IllegalStateException("The length of branchQulifier not equals to 16.");
}
byte[] branch = new byte[BRANCH_QUALIFIER_LENGTH];
System.arraycopy(unique, 0, branch, 0, branch.length);
return new TransactionXid(XidFactory.JTA_FORMAT_ID, global, branch);
}
public TransactionXid createBranchXid(TransactionXid globalXid, byte[] branchQualifier) {
if (globalXid == null) {
throw new IllegalArgumentException("Xid cannot be null.");
} else if (globalXid.getGlobalTransactionId() == null) {
throw new IllegalArgumentException("The globalTransactionId cannot be null.");
} else if (globalXid.getGlobalTransactionId().length > TransactionXid.MAXGTRIDSIZE) {
throw new IllegalArgumentException("The length of globalTransactionId cannot exceed 64 bytes.");
}
if (branchQualifier == null) {
throw new IllegalArgumentException("The branchQulifier cannot be null.");
} else if (branchQualifier.length > TransactionXid.MAXBQUALSIZE) {
throw new IllegalArgumentException("The length of branchQulifier cannot exceed 64 bytes.");
}
byte[] global = new byte[globalXid.getGlobalTransactionId().length];
System.arraycopy(globalXid.getGlobalTransactionId(), 0, global, 0, global.length);
return new TransactionXid(XidFactory.JTA_FORMAT_ID, global, branchQualifier);
}
public byte[] generateUniqueKey() {
byte[] byteArray = new byte[16];
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
int year = calendar.get(Calendar.YEAR) - 2014;
int month = calendar.get(Calendar.MONTH) + 1;
int day = calendar.get(Calendar.DAY_OF_MONTH);
int hour = calendar.get(Calendar.HOUR_OF_DAY);
int minute = calendar.get(Calendar.MINUTE);
int second = calendar.get(Calendar.SECOND);
int millis = calendar.get(Calendar.MILLISECOND);
int value = (year << 29) >>> 1;
value = value | (month << 24);
value = value | (day << 19);
value = value | (hour << 14);
value = value | (minute << 8);
value = value | (second << 2);
value = value | (millis >>> 8);
byte[] valueByteArray = ByteUtils.intToByteArray(value);
byte[] timeByteArray = new byte[5];
System.arraycopy(valueByteArray, 0, timeByteArray, 0, 4);
timeByteArray[4] = (byte) (((millis << 24) >>> 24) + Byte.MIN_VALUE);
byte increment = (byte) atomic.incrementAndGet();
byte[] randomByteArray = new byte[4];
random.nextBytes(randomByteArray);
System.arraycopy(timeByteArray, 0, byteArray, 0, timeByteArray.length);
System.arraycopy(hardwareAddress, 0, byteArray, timeByteArray.length, SIZE_OF_MAC);
byteArray[SIZE_OF_MAC + 5] = increment;
System.arraycopy(randomByteArray, 0, byteArray, SIZE_OF_MAC + 5 + 1, randomByteArray.length);
return byteArray;
}
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/common/utils/ByteUtils.java
================================================
/**
* Copyright 2014-2016 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.common.utils;
public class ByteUtils {
public static short byteArrayToShort(final byte[] buf) {
return byteArrayToShort(buf, 0);
}
public static int byteArrayToInt(final byte[] buf) {
return byteArrayToInt(buf, 0);
}
public static long byteArrayToLong(final byte[] buf) {
return byteArrayToLong(buf, 0);
}
public static long byteArrayToLong(final byte[] buf, final int start) {
if (start < 0 || start + 7 >= buf.length) {
throw new IllegalArgumentException();
}
long value = ((long) buf[start] & 0xff) << 56;
value |= ((long) buf[start + 1] & 0xff) << 48;
value |= ((long) buf[start + 2] & 0xff) << 40;
value |= ((long) buf[start + 3] & 0xff) << 32;
value |= ((long) buf[start + 4] & 0xff) << 24;
value |= ((long) buf[start + 5] & 0xff) << 16;
value |= ((long) buf[start + 6] & 0xff) << 8;
value |= ((long) buf[start + 7] & 0xff);
return value;
}
public static byte[] longToByteArray(final long value) {
final byte[] result = new byte[8];
result[7] = (byte) (value & 0xff);
result[6] = (byte) (value >> 8 & 0xff);
result[5] = (byte) (value >> 16 & 0xff);
result[4] = (byte) (value >> 24 & 0xff);
result[3] = (byte) (value >> 32 & 0xff);
result[2] = (byte) (value >> 40 & 0xff);
result[1] = (byte) (value >> 48 & 0xff);
result[0] = (byte) (value >> 56 & 0xff);
return result;
}
public static int byteArrayToInt(final byte[] buf, final int start) {
if (start < 0 || start + 3 >= buf.length) {
throw new IllegalArgumentException();
}
int value = (buf[start] & 0xff) << 24;
value |= (buf[start + 1] & 0xff) << 16;
value |= (buf[start + 2] & 0xff) << 8;
value |= (buf[start + 3] & 0xff);
return value;
}
public static byte[] intToByteArray(final int value) {
final byte[] result = new byte[4];
result[3] = (byte) (value & 0xff);
result[2] = (byte) (value >> 8 & 0xff);
result[1] = (byte) (value >> 16 & 0xff);
result[0] = (byte) (value >> 24 & 0xff);
return result;
}
public static short byteArrayToShort(final byte[] buf, final int start) {
if (start < 0 || start + 1 >= buf.length) {
throw new IllegalArgumentException();
}
int value = (buf[start] & 0xff) << 8;
value = value | (buf[start + 1] & 0xff);
return (short) value;
}
public static byte[] shortToByteArray(final short value) {
final byte[] result = new byte[2];
result[1] = (byte) (value & 0xff);
result[0] = (byte) (value >> 8 & 0xff);
return result;
}
public static String byteArrayToString(final byte[] bytes, final int startIndex, final int len) {
StringBuilder ber = new StringBuilder();
for (int i = startIndex, j = 0; j < len; i++, j++) {
byte b = bytes[i];
ber.append(CHARS[(b & 0xf0) >> 4]);
ber.append(CHARS[(b & 0x0f)]);
}
return ber.toString();
}
public static String byteArrayToString(final byte[] bytes) {
return byteArrayToString(bytes, 0, bytes.length);
}
public static byte[] stringToByteArray(String str) {
if (str == null) {
return new byte[0];
} else if (str.length() % 2 == 1) {
throw new IllegalArgumentException();
}
char[] array = str.toCharArray();
byte[] bytes = new byte[array.length / 2];
for (int i = 0; i < array.length; i = i + 2) {
int index1 = indexOf(array[i]);
int index2 = indexOf(array[i + 1]);
int tempval = index1 << 4;
int byteval = tempval | index2;
bytes[i / 2] = (byte) byteval;
}
return bytes;
}
private static int indexOf(char chr) {
if (chr >= DIGIT_START && chr <= DIGIT_END) {
return chr - DIGIT_START;
} else if (chr >= LETTER_START && chr <= LETTER_END) {
return chr - LETTER_START + 10;
} else if (chr >= UPPER_LETTER_START && chr <= UPPER_LETTER_END) {
return chr - UPPER_LETTER_START + 10;
} else {
throw new IllegalArgumentException();
}
}
static final char DIGIT_START = '0';
static final char DIGIT_END = '9';
static final char LETTER_START = 'a';
static final char LETTER_END = 'f';
static final char UPPER_LETTER_START = 'A';
static final char UPPER_LETTER_END = 'F';
static final char[] CHARS = new char[] {
//
'0', '1', '2', '3', '4', '5', '6', '7',//
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' //
};
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/common/utils/CommonUtils.java
================================================
/**
* Copyright 2014-2016 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.common.utils;
import java.io.Closeable;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.bytesoft.transaction.remote.RemoteAddr;
import org.bytesoft.transaction.remote.RemoteNode;
import org.bytesoft.transaction.remote.RemoteSvc;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CommonUtils {
static final Logger logger = LoggerFactory.getLogger(CommonUtils.class);
public static RemoteAddr getRemoteAddr(String identifier) {
if (StringUtils.isBlank(identifier)) {
return null;
} else {
String[] values = identifier.split("\\s*:\\s*");
if (values.length != 3) {
return null;
}
RemoteAddr remoteAddr = new RemoteAddr();
remoteAddr.setServerHost(values[0]);
remoteAddr.setServerPort(Integer.parseInt(values[2]));
return remoteAddr;
}
}
public static RemoteNode getRemoteNode(String identifier) {
if (StringUtils.isBlank(identifier)) {
return null;
} else {
String[] values = identifier.split("\\s*:\\s*");
if (values.length != 3) {
return null;
}
RemoteNode remoteNode = new RemoteNode();
remoteNode.setServerHost(values[0]);
remoteNode.setServiceKey(values[1]);
remoteNode.setServerPort(Integer.parseInt(values[2]));
return remoteNode;
}
}
public static RemoteSvc getRemoteSvc(RemoteNode remoteNode) {
RemoteSvc remoteSvc = new RemoteSvc();
remoteSvc.setServerHost(remoteNode.getServerHost());
remoteSvc.setServiceKey(remoteNode.getServiceKey());
remoteSvc.setServerPort(remoteNode.getServerPort());
return remoteSvc;
}
public static RemoteSvc getRemoteSvc(String identifier) {
RemoteNode remoteNode = getRemoteNode(identifier);
return getRemoteSvc(remoteNode);
}
public static String getApplication(String identifier) {
if (StringUtils.isBlank(identifier)) {
return null;
} else {
String[] values = identifier.split("\\s*:\\s*");
return values.length == 3 ? values[1] : null;
}
}
public static String getInstanceKey(String identifier) {
RemoteNode remoteNode = CommonUtils.getRemoteNode(identifier);
if (remoteNode == null) {
return null;
} else {
return String.format("%s:%s", remoteNode.getServerHost(), remoteNode.getServerPort());
}
}
public static boolean applicationEquals(String source, String target) {
String sourceApplication = CommonUtils.getApplication(source);
String targetApplication = CommonUtils.getApplication(target);
if (StringUtils.isBlank(sourceApplication) || StringUtils.isBlank(targetApplication)) {
return false;
} else {
return StringUtils.equalsIgnoreCase(sourceApplication, targetApplication);
}
}
public static boolean instanceKeyEquals(String source, String target) {
RemoteAddr sourceAddr = CommonUtils.getRemoteAddr(source);
RemoteAddr targetAddr = CommonUtils.getRemoteAddr(target);
if (sourceAddr == null || targetAddr == null) {
return false;
} else {
String sourceHost = sourceAddr.getServerHost();
String targetHost = targetAddr.getServerHost();
int sourcePort = sourceAddr.getServerPort();
int targetPort = targetAddr.getServerPort();
return StringUtils.equalsIgnoreCase(sourceHost, targetHost) && sourcePort == targetPort;
}
}
public static boolean equals(Object o1, Object o2) {
return java.util.Objects.equals(o1, o2);
}
public static void closeQuietly(Closeable closeable) {
IOUtils.closeQuietly(closeable);
}
public static String getInetAddress() {
Enumeration interfaces = null;
try {
interfaces = NetworkInterface.getNetworkInterfaces();
} catch (SocketException ignored) {
return CommonUtils.getLocalHostAddress();
}
final Set virtualist = new TreeSet(new InetAddrComparator());
final Set candidates = new TreeSet(new InetAddrComparator());
while (interfaces != null && interfaces.hasMoreElements()) {
NetworkInterface network = interfaces.nextElement();
try {
if (network.isUp() == false || network.isLoopback() || network.isPointToPoint()) {
continue;
}
} catch (SocketException ignored) {
continue;
}
Enumeration addresses = network.getInetAddresses();
while (addresses.hasMoreElements()) {
InetAddress address = addresses.nextElement();
if (Inet4Address.class.isInstance(address) == false) {
continue;
} else if (address.isAnyLocalAddress() || address.isMulticastAddress()) {
continue;
}
(network.isVirtual() ? virtualist : candidates).add(address);
}
}
Iterator itr = candidates.iterator();
Iterator vtr = virtualist.iterator();
if (itr.hasNext()) {
return itr.next().getHostAddress();
} else if (vtr.hasNext()) {
return vtr.next().getHostAddress();
}
return CommonUtils.getLocalHostAddress();
}
private static class InetAddrComparator implements Comparator {
public int compare(InetAddress o1, InetAddress o2) {
boolean linkLocalAddr1 = o1.isLinkLocalAddress();
boolean siteLocalAddr1 = o1.isSiteLocalAddress();
boolean linkLocalAddr2 = o2.isLinkLocalAddress();
boolean siteLocalAddr2 = o2.isSiteLocalAddress();
boolean linkLocalEquals = linkLocalAddr1 == linkLocalAddr2;
boolean siteLocalEquals = siteLocalAddr1 == siteLocalAddr2;
if (linkLocalEquals && siteLocalEquals) {
return -1;
} else if (linkLocalEquals == false && siteLocalEquals == false) {
return linkLocalAddr1 ? -1 : 1;
} else if (siteLocalEquals) {
return linkLocalAddr1 ? -1 : 1;
} else if (linkLocalEquals) {
return siteLocalAddr1 ? -1 : 1;
} else {
return -1;
}
}
}
public static String getInetAddress(String host) {
try {
InetAddress inetAddr = InetAddress.getByName(host);
return inetAddr.getHostAddress();
} catch (UnknownHostException ex) {
logger.error("Error occurred while getting ip address: host= {}.", host, ex);
return host;
}
}
public static String getLocalHostAddress() {
try {
InetAddress inetAddr = InetAddress.getLocalHost();
return inetAddr.getHostAddress();
} catch (Exception ex) {
logger.error("Error occurred while getting ip address.", ex);
return "127.0.0.1";
}
}
}
================================================
FILE: bytejta-core/src/main/java/org/bytesoft/common/utils/SerializeUtils.java
================================================
/**
* Copyright 2014-2018 yangming.liu.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, see .
*/
package org.bytesoft.common.utils;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.objenesis.strategy.SerializingInstantiatorStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.caucho.hessian.io.HessianInput;
import com.caucho.hessian.io.HessianOutput;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.Kryo.DefaultInstantiatorStrategy;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.esotericsoftware.kryo.pool.KryoCallback;
import com.esotericsoftware.kryo.pool.KryoFactory;
import com.esotericsoftware.kryo.pool.KryoPool;
public class SerializeUtils {
static final Logger logger = LoggerFactory.getLogger(SerializeUtils.class);
static final String SERIALIZER_NAME_DEFAULT = "default";
static final String SERIALIZER_NAME_KRYO = "kryo";
static final String SERIALIZER_NAME_HESSIAN = "hessian";
static final int SERIALIZER_DEFAULT = 0x0;
static final int SERIALIZER_KRYO = 0x1;
static final int SERIALIZER_HESSIAN = 0x2;
static int PREFERRED_SERIALIZER = SERIALIZER_KRYO;
static {
String serializer = StringUtils.trimToNull(System.getProperty("bytejta.serializer.preferred"));
if (StringUtils.isNotBlank(serializer) && StringUtils.equalsIgnoreCase(SERIALIZER_NAME_KRYO, serializer)) {
PREFERRED_SERIALIZER = SERIALIZER_KRYO;
} else if (StringUtils.isNotBlank(serializer) && StringUtils.equalsIgnoreCase(SERIALIZER_NAME_HESSIAN, serializer)) {
PREFERRED_SERIALIZER = SERIALIZER_HESSIAN;
} else if (StringUtils.isNotBlank(serializer)) {
PREFERRED_SERIALIZER = SERIALIZER_DEFAULT;
}
}
static KryoPool kryoPool = new KryoPool.Builder(new KryoFactory() {
public Kryo create() {
Kryo kryo = new Kryo();
kryo.setInstantiatorStrategy(new DefaultInstantiatorStrategy(new SerializingInstantiatorStrategy()));
return kryo;
}
}).softReferences().build();
public static byte[] serializeObject(Serializable obj, int serializerType) throws IOException {
int serializer = SERIALIZER_DEFAULT;
byte[] dataArray = null;
if (serializerType == SERIALIZER_KRYO) {
dataArray = kryoSerialize(obj);
serializer = SERIALIZER_KRYO;
} else if (serializerType == SERIALIZER_HESSIAN) {
dataArray = hessianSerialize(obj);
serializer = SERIALIZER_HESSIAN;
} else {
dataArray = javaSerialize(obj);
serializer = SERIALIZER_DEFAULT;
}
byte[] byteArray = new byte[dataArray.length + 1];
byteArray[0] = (byte) serializer;
System.arraycopy(dataArray, 0, byteArray, 1, dataArray.length);
return byteArray;
}
public static byte[] serializeObject(Serializable obj) throws IOException {
if (PREFERRED_SERIALIZER == SERIALIZER_DEFAULT) {
return serializeObject(obj, PREFERRED_SERIALIZER);
} else {
try {
return serializeObject(obj, PREFERRED_SERIALIZER);
} catch (Exception ex) {
return serializeObject(obj, SERIALIZER_DEFAULT);
}
}
}
public static Serializable deserializeObject(byte[] bytes) throws IOException {
if (bytes.length == 0) {
throw new IllegalArgumentException();
}
byte[] byteArray = new byte[bytes.length - 1];
int serializer = bytes[0];
if (serializer == SERIALIZER_KRYO) {
System.arraycopy(bytes, 1, byteArray, 0, byteArray.length);
return kryoDeserialize(byteArray);
} else if (serializer == SERIALIZER_HESSIAN) {
System.arraycopy(bytes, 1, byteArray, 0, byteArray.length);
return hessianDeserialize(byteArray);
} else if (serializer == SERIALIZER_DEFAULT) {
System.arraycopy(bytes, 1, byteArray, 0, byteArray.length);
return javaDeserialize(byteArray);
} else {
throw new IllegalArgumentException();
}
}
public static byte[] javaSerialize(final Serializable obj) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(baos));
try {
oos.writeObject(obj);
} finally {
CommonUtils.closeQuietly(oos);
}
return baos.toByteArray();
}
public static Serializable javaDeserialize(byte[] byteArray) throws IOException {
ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(new ByteArrayInputStream(byteArray)));
try {
return (Serializable) ois.readObject();
} catch (ClassNotFoundException ex) {
throw new IllegalStateException(ex);
} finally {
CommonUtils.closeQuietly(ois);
}
}
public static byte[] kryoSerialize(final Serializable obj) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
final Output output = new Output(baos);
try {
kryoPool.run(new KryoCallback