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.bytesoft bytejta-supports-springcloud 0.5.0-BETA9 ``` ###### 1.1.2. dubbo ```xml org.bytesoft bytejta-supports-dubbo 0.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.0 org.bytesoft bytejta-parent 0.5.0-BETA9 bytejta-core jar bytejta-core The bytejta-core project is the core module of ByteJTA. http://www.bytesoft.org UTF-8 javax.transaction javax.transaction-api javax.jms javax.jms-api javax.resource javax.resource-api org.slf4j slf4j-api org.apache.commons commons-lang3 commons-io commons-io com.caucho hessian com.esotericsoftware kryo javax.annotation javax.annotation-api javax.inject javax.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 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 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() { public Object execute(Kryo kryo) { kryo.writeClassAndObject(output, obj); return null; } }); } finally { CommonUtils.closeQuietly(output); } return baos.toByteArray(); } public static Serializable kryoDeserialize(byte[] byteArray) throws IOException { final Input input = new Input(new ByteArrayInputStream(byteArray)); try { return kryoPool.run(new KryoCallback() { public Serializable execute(Kryo kryo) { return (Serializable) kryo.readClassAndObject(input); } }); } finally { CommonUtils.closeQuietly(input); } } public static byte[] hessianSerialize(Serializable obj) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); HessianOutput ho = new HessianOutput(baos); try { ho.writeObject(obj); return baos.toByteArray(); } finally { CommonUtils.closeQuietly(baos); } } public static Serializable hessianDeserialize(byte[] bytes) throws IOException { ByteArrayInputStream bais = new ByteArrayInputStream(bytes); HessianInput hi = new HessianInput(bais); try { Object result = hi.readObject(); return (Serializable) result; } finally { CommonUtils.closeQuietly(bais); } } public static String serializeClass(Class clazz) { if (boolean.class.equals(clazz)) { return "Z"; } else if (byte.class.equals(clazz)) { return "B"; } else if (short.class.equals(clazz)) { return "S"; } else if (char.class.equals(clazz)) { return "C"; } else if (int.class.equals(clazz)) { return "I"; } else if (float.class.equals(clazz)) { return "F"; } else if (long.class.equals(clazz)) { return "J"; } else if (double.class.equals(clazz)) { return "D"; } else if (void.class.equals(clazz)) { return "V"; } else if (clazz.isArray()) { return clazz.getName(); } else { return String.format("L%s;", clazz.getName().replaceAll("\\.", "/")); } } public static Class deserializeClass(String classDesc) { String clazz = StringUtils.trimToEmpty(classDesc); if (StringUtils.isBlank(clazz)) { throw new IllegalStateException(); } if (clazz.length() > 1) { String clazzName = clazz.replaceAll("\\/", "."); ClassLoader cl = Thread.currentThread().getContextClassLoader(); try { return cl.loadClass(clazzName); } catch (ClassNotFoundException ex) { throw new IllegalStateException(ex.getMessage()); } } final char character = clazz.charAt(0); return SerializeUtils.deserializeClass(character); } public static Class deserializeClass(final char character) { switch (character) { case 'Z': return boolean.class; case 'B': return byte.class; case 'S': return short.class; case 'C': return char.class; case 'I': return int.class; case 'J': return long.class; case 'F': return float.class; case 'D': return double.class; default: throw new IllegalStateException(); } } public static String serializeMethod(Method method) { StringBuilder ber = new StringBuilder(); ber.append(method.getName()).append("("); Class[] parameterTypes = method.getParameterTypes(); for (int i = 0; i < parameterTypes.length; i++) { Class parameterType = parameterTypes[i]; String clazzName = SerializeUtils.serializeClass(parameterType); ber.append(clazzName); } ber.append(")").append(SerializeUtils.serializeClass(method.getReturnType())); return ber.toString(); } public static Method deserializeMethod(Class interfaceClass, String methodDesc) throws Exception { int startIdx = methodDesc.indexOf("("); String methodName = methodDesc.substring(0, startIdx); int endIndex = methodDesc.indexOf(")"); String value = methodDesc.substring(startIdx + 1, endIndex); char[] values = value.toCharArray(); List> paramTypeList = new ArrayList>(); boolean flags = false; StringBuilder clazzDesc = new StringBuilder(); for (int i = 0; i < values.length; i++) { char character = values[i]; if (character == ';') { flags = false; String paramTypeNameDesc = clazzDesc.toString(); clazzDesc.delete(0, clazzDesc.length()); Class paramType = SerializeUtils.deserializeClass(paramTypeNameDesc); paramTypeList.add(paramType); continue; } else if (flags) { clazzDesc.append(character); continue; } else if (character == 'L') { flags = true; continue; } Class paramType = SerializeUtils.deserializeClass(character); paramTypeList.add(paramType); } Class[] parameterTypes = new Class[paramTypeList.size()]; paramTypeList.toArray(parameterTypes); return interfaceClass.getDeclaredMethod(methodName, parameterTypes); } } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/CommitRequiredException.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.transaction; import javax.transaction.SystemException; public class CommitRequiredException extends SystemException { private static final long serialVersionUID = 1L; public CommitRequiredException() { super(); } public CommitRequiredException(String s) { super(s); } public CommitRequiredException(int errcode) { super(errcode); } } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/RemoteSystemException.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.transaction; import java.rmi.RemoteException; import javax.transaction.SystemException; public class RemoteSystemException extends SystemException { private static final long serialVersionUID = 1L; public RemoteSystemException() { super(); } public RemoteSystemException(String s) { super(s); } public RemoteSystemException(int errcode) { super(errcode); } public Throwable initCause(Throwable cause) { if (RemoteException.class.isInstance(cause) == false) { throw new IllegalArgumentException(); } return super.initCause(cause); } public RemoteException getRemoteException() { Throwable thrown = super.getCause(); return RemoteException.class.cast(thrown); } } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/RollbackRequiredException.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.transaction; import javax.transaction.SystemException; public class RollbackRequiredException extends SystemException { private static final long serialVersionUID = 1L; public RollbackRequiredException() { super(); } public RollbackRequiredException(String s) { super(s); } public RollbackRequiredException(int errcode) { super(errcode); } } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/Transaction.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.transaction; import javax.transaction.HeuristicMixedException; import javax.transaction.HeuristicRollbackException; import javax.transaction.RollbackException; import javax.transaction.SystemException; import org.bytesoft.transaction.archive.TransactionArchive; 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; public interface Transaction extends javax.transaction.Transaction, TransactionExtra { public void fireBeforeTransactionCompletion() throws RollbackRequiredException, SystemException; public void fireBeforeTransactionCompletionQuietly(); public void fireAfterTransactionCompletion(); public boolean isLocalTransaction(); public boolean isMarkedRollbackOnly(); public void setRollbackOnlyQuietly(); public int getTransactionStatus(); public void setTransactionStatus(int status); public void resume() throws SystemException; public void suspend() throws SystemException; public boolean isTiming(); public void setTransactionTimeout(int seconds); public void registerTransactionListener(TransactionListener listener); public void registerTransactionResourceListener(TransactionResourceListener listener); public TransactionExtra getTransactionalExtra(); public void setTransactionalExtra(TransactionExtra transactionalExtra); public XAResourceDescriptor getResourceDescriptor(String beanName); public XAResourceDescriptor getRemoteCoordinator(RemoteSvc remoteSvc); public XAResourceDescriptor getRemoteCoordinator(String application); public TransactionContext getTransactionContext(); public TransactionArchive getTransactionArchive(); public int participantPrepare() throws RollbackRequiredException, CommitRequiredException; public void participantCommit(boolean opc) throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException, CommitRequiredException, SystemException; public void participantRollback() throws IllegalStateException, RollbackRequiredException, SystemException; public void forget() throws SystemException; public void forgetQuietly(); public void recover() throws SystemException; public void recoveryCommit() throws CommitRequiredException, SystemException; public void recoveryRollback() throws RollbackRequiredException, SystemException; public Exception getCreatedAt(); } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/TransactionBeanFactory.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.transaction; import org.bytesoft.transaction.logging.ArchiveDeserializer; import org.bytesoft.transaction.logging.TransactionLogger; 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 interface TransactionBeanFactory { public TransactionLock getTransactionLock(); public TransactionManager getTransactionManager(); public XidFactory getXidFactory(); public TransactionTimer getTransactionTimer(); public TransactionRepository getTransactionRepository(); public TransactionInterceptor getTransactionInterceptor(); public TransactionRecovery getTransactionRecovery(); public TransactionParticipant getNativeParticipant(); public TransactionLogger getTransactionLogger(); public ArchiveDeserializer getArchiveDeserializer(); public XAResourceDeserializer getResourceDeserializer(); } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/TransactionContext.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.transaction; import java.io.Serializable; import org.bytesoft.transaction.xa.TransactionXid; public class TransactionContext implements Serializable, Cloneable { private static final long serialVersionUID = 1L; protected transient Object propagatedBy; protected transient boolean propagated; protected transient boolean coordinator; protected transient boolean recoveried; protected transient int recoveredTimes; protected TransactionXid xid; protected long createdTime; protected long expiredTime; protected boolean rollbackOnly; protected long configFlags; public TransactionContext clone() { TransactionContext that = new TransactionContext(); that.xid = this.xid.clone(); that.createdTime = System.currentTimeMillis(); that.expiredTime = this.expiredTime; that.rollbackOnly = this.rollbackOnly; that.configFlags = this.configFlags; return that; } public int getRecoveredTimes() { return recoveredTimes; } public void setRecoveredTimes(int recoveredTimes) { this.recoveredTimes = recoveredTimes; } public boolean isCoordinator() { return coordinator; } public void setCoordinator(boolean coordinator) { this.coordinator = coordinator; } public boolean isRecoveried() { return recoveried; } public void setRecoveried(boolean recoveried) { this.recoveried = recoveried; } public TransactionXid getXid() { return xid; } public void setXid(TransactionXid xid) { this.xid = xid; } public long getCreatedTime() { return createdTime; } public void setCreatedTime(long createdTime) { this.createdTime = createdTime; } public long getExpiredTime() { return expiredTime; } public void setExpiredTime(long expiredTime) { this.expiredTime = expiredTime; } public long getConfigFlags() { return configFlags; } public void setConfigFlags(long configFlags) { this.configFlags = configFlags; } public boolean isPropagated() { return propagated; } public void setPropagated(boolean propagated) { this.propagated = propagated; } public Object getPropagatedBy() { return propagatedBy; } public void setPropagatedBy(Object propagatedBy) { this.propagatedBy = propagatedBy; } public boolean isRollbackOnly() { return rollbackOnly; } public void setRollbackOnly(boolean rollbackOnly) { this.rollbackOnly = rollbackOnly; } } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/TransactionException.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.transaction; public class TransactionException extends RuntimeException { private static final long serialVersionUID = 1L; /* XAException.errorCode */ public int errorCode; public TransactionException(int errcode) { super(); errorCode = errcode; } } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/TransactionLock.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.transaction; import org.bytesoft.transaction.xa.TransactionXid; public interface TransactionLock { public boolean lockTransaction(TransactionXid transactionXid, String identifier); public void unlockTransaction(TransactionXid transactionXid, String identifier); } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/TransactionManager.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.transaction; import javax.transaction.SystemException; public interface TransactionManager extends javax.transaction.TransactionManager { public int getTimeoutSeconds(); public void setTimeoutSeconds(int timeoutSeconds); public void associateThread(Transaction transaction); public Transaction desociateThread(); public Transaction getTransaction(Thread thread); public Transaction getTransactionQuietly(); public Transaction getTransaction() throws SystemException; public Transaction suspend() throws SystemException; public void setRollbackOnlyQuietly(); } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/TransactionParticipant.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.transaction; import javax.transaction.xa.XAException; import javax.transaction.xa.XAResource; import javax.transaction.xa.Xid; public interface TransactionParticipant extends XAResource { public Transaction end(TransactionContext transactionContext, int flags) throws XAException; public void forgetQuietly(Xid xid); public Transaction start(TransactionContext transactionContext, int flags) throws XAException; } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/TransactionRecovery.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.transaction; import org.bytesoft.transaction.archive.TransactionArchive; public interface TransactionRecovery { public Transaction reconstruct(TransactionArchive archive); public void timingRecover(); public void startRecovery(); public void branchRecover(); public boolean isInitialized(); } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/TransactionRepository.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.transaction; import java.util.List; import org.bytesoft.transaction.xa.TransactionXid; public interface TransactionRepository { // active-transaction & error-transaction public void putTransaction(TransactionXid xid, Transaction transaction); public Transaction getTransaction(TransactionXid xid); public Transaction removeTransaction(TransactionXid xid); // error-transaction public void putErrorTransaction(TransactionXid xid, Transaction transaction); public Transaction getErrorTransaction(TransactionXid xid); public Transaction removeErrorTransaction(TransactionXid xid); public List getErrorTransactionList(); public List getActiveTransactionList(); } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/adapter/ResourceAdapterImpl.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.transaction.adapter; import java.util.ArrayList; import java.util.List; import javax.resource.ResourceException; import javax.resource.spi.ActivationSpec; import javax.resource.spi.BootstrapContext; import javax.resource.spi.ResourceAdapter; import javax.resource.spi.ResourceAdapterInternalException; import javax.resource.spi.endpoint.MessageEndpointFactory; import javax.resource.spi.work.Work; import javax.resource.spi.work.WorkException; import javax.resource.spi.work.WorkManager; import javax.transaction.xa.XAResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ResourceAdapterImpl implements ResourceAdapter { static final Logger logger = LoggerFactory.getLogger(ResourceAdapterImpl.class); private List workList = new ArrayList();; private WorkManager workManager; public void start(BootstrapContext ctx) throws ResourceAdapterInternalException { this.workManager = ctx.getWorkManager(); for (int i = 0; this.workList != null && i < this.workList.size(); i++) { Work work = this.workList.get(i); try { this.workManager.startWork(work); } catch (WorkException ex) { this.stop(); throw new ResourceAdapterInternalException(ex); } catch (RuntimeException ex) { this.stop(); throw new ResourceAdapterInternalException(ex); } } } public void stop() { for (int i = 0; this.workList != null && i < this.workList.size(); i++) { Work work = this.workList.get(i); try { work.release(); } catch (RuntimeException rex) { logger.debug(rex.getMessage(), rex); } } } public void endpointActivation(MessageEndpointFactory endpointFactory, ActivationSpec spec) throws ResourceException { } public void endpointDeactivation(MessageEndpointFactory endpointFactory, ActivationSpec spec) { } public XAResource[] getXAResources(ActivationSpec[] specs) throws ResourceException { return new XAResource[0]; } public List getWorkList() { return workList; } public void setWorkList(List workList) { this.workList = workList; } } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/archive/TransactionArchive.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.transaction.archive; import java.util.ArrayList; import java.util.List; import javax.transaction.xa.Xid; public class TransactionArchive { private transient String endpoint; private Xid xid; private int status; private int vote; private boolean coordinator; private Object propagatedBy; private final List nativeResources = new ArrayList(); private final List remoteResources = new ArrayList(); private int transactionStrategyType; private XAResourceArchive optimizedResource; private int recoveredTimes; private long recoveredAt; public int getRecoveredTimes() { return recoveredTimes; } public void setRecoveredTimes(int recoveredTimes) { this.recoveredTimes = recoveredTimes; } public long getRecoveredAt() { return recoveredAt; } public void setRecoveredAt(long recoveredAt) { this.recoveredAt = recoveredAt; } public String getEndpoint() { return endpoint; } public void setEndpoint(String endpoint) { this.endpoint = endpoint; } public Xid getXid() { return xid; } public void setXid(Xid xid) { this.xid = xid; } public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } public int getVote() { return vote; } public void setVote(int vote) { this.vote = vote; } public boolean isCoordinator() { return coordinator; } public void setCoordinator(boolean coordinator) { this.coordinator = coordinator; } public Object getPropagatedBy() { return propagatedBy; } public void setPropagatedBy(Object propagatedBy) { this.propagatedBy = propagatedBy; } public List getNativeResources() { return nativeResources; } public List getRemoteResources() { return remoteResources; } public XAResourceArchive getOptimizedResource() { return optimizedResource; } public void setOptimizedResource(XAResourceArchive optimizedResource) { this.optimizedResource = optimizedResource; } public int getTransactionStrategyType() { return transactionStrategyType; } public void setTransactionStrategyType(int transactionStrategyType) { this.transactionStrategyType = transactionStrategyType; } } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/archive/XAResourceArchive.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.transaction.archive; 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 XAResourceArchive implements XAResource { static final Logger logger = LoggerFactory.getLogger(XAResourceArchive.class); public static final int DEFAULT_VOTE = -1; private boolean suspended; private boolean delisted; private boolean completed; private boolean readonly; private boolean committed; private boolean rolledback; private boolean heuristic; private boolean identified; private transient boolean recovered; private Xid xid; private int vote = DEFAULT_VOTE; private XAResourceDescriptor descriptor; private XAResourceDescriptor stickiness; public void commit(Xid ignore, boolean onePhase) throws XAException { if (this.readonly) { // ignore } else if (this.committed) { // ignore } else if (this.rolledback) { throw new XAException(XAException.XA_HEURRB); } else { descriptor.commit(xid, onePhase); } } public void end(Xid ignore, int flags) throws XAException { descriptor.end(xid, flags); } public void forget(Xid ignore) throws XAException { descriptor.forget(xid); } public void forgetQuietly(Xid ignore) { try { descriptor.forget(xid); } catch (XAException ex) { logger.warn("Error occurred while forgeting xa-resource.", xid); } } public int getTransactionTimeout() throws XAException { return descriptor.getTransactionTimeout(); } public boolean isSameRM(XAResource xares) throws XAException { if (XAResourceArchive.class.isInstance(xares)) { XAResourceArchive archive = (XAResourceArchive) xares; return descriptor.isSameRM(archive.getDescriptor()); } else { return descriptor.isSameRM(xares); } } public int prepare(Xid ignore) throws XAException { if (this.vote == -1) { this.vote = this.descriptor.prepare(this.xid); this.readonly = this.vote == XAResource.XA_RDONLY; } return this.vote; } public Xid[] recover(int flag) throws XAException { return descriptor.recover(flag); } public void rollback(Xid ignore) throws XAException { if (this.readonly) { // ignore } else if (this.committed) { throw new XAException(XAException.XA_HEURCOM); } else if (this.rolledback) { // ignore } else { descriptor.rollback(xid); } } public boolean setTransactionTimeout(int seconds) throws XAException { return descriptor.setTransactionTimeout(seconds); } public void start(Xid ignore, int flags) throws XAException { descriptor.start(xid, flags); } public String toString() { return String.format("xa-res-archive[descriptor: %s]", this.descriptor); } public XAResourceDescriptor getStickiness() { return stickiness; } public void setStickiness(XAResourceDescriptor stickiness) { this.stickiness = stickiness; } public XAResourceDescriptor getDescriptor() { return descriptor; } public void setDescriptor(XAResourceDescriptor descriptor) { this.descriptor = descriptor; } public boolean isSuspended() { return suspended; } public void setSuspended(boolean suspended) { this.suspended = suspended; } public boolean isDelisted() { return delisted; } public void setDelisted(boolean delisted) { this.delisted = delisted; } public Xid getXid() { return xid; } public void setXid(Xid xid) { this.xid = xid; } public int getVote() { return vote; } public void setVote(int vote) { this.vote = vote; } public boolean isCompleted() { return completed; } public void setCompleted(boolean completed) { this.completed = completed; } public boolean isCommitted() { return committed; } public void setCommitted(boolean committed) { this.committed = committed; } public boolean isRolledback() { return rolledback; } public void setRolledback(boolean rolledback) { this.rolledback = rolledback; } public boolean isReadonly() { return readonly; } public void setReadonly(boolean readonly) { this.readonly = readonly; } public boolean isHeuristic() { return heuristic; } public void setHeuristic(boolean heuristic) { this.heuristic = heuristic; } public boolean isRecovered() { return recovered; } public void setRecovered(boolean recovered) { this.recovered = recovered; } public boolean isIdentified() { return identified; } public void setIdentified(boolean identified) { this.identified = identified; } } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/aware/TransactionBeanFactoryAware.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.transaction.aware; import org.bytesoft.transaction.TransactionBeanFactory; public interface TransactionBeanFactoryAware { public static final String BEAN_FACTORY_FIELD_NAME = "beanFactory"; public TransactionBeanFactory getBeanFactory(); public void setBeanFactory(TransactionBeanFactory tbf); } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/aware/TransactionDebuggable.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.transaction.aware; public interface TransactionDebuggable { public boolean isDebuggingEnabled(); public void setDebuggingEnabled(boolean debuggingEnabled); } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/aware/TransactionEndpointAware.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.transaction.aware; public interface TransactionEndpointAware { public static final String ENDPOINT_FIELD_NAME = "endpoint"; public String getEndpoint(); public void setEndpoint(String identifier); } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/cmd/CommandDispatcher.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.transaction.cmd; import java.util.concurrent.Callable; public interface CommandDispatcher { public Object dispatch(Callable callable) throws Exception; public void dispatch(Runnable runnable) throws Exception; } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/internal/SynchronizationImpl.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.transaction.internal; import javax.transaction.Synchronization; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SynchronizationImpl implements Synchronization { static final Logger logger = LoggerFactory.getLogger(SynchronizationImpl.class); private Synchronization delegate; private boolean beforeRequired; private boolean finishRequired; public SynchronizationImpl(Synchronization sync) { if (sync == null) { throw new IllegalArgumentException(); } else { this.delegate = sync; this.beforeRequired = true; this.finishRequired = true; } } public void beforeCompletion() { if (this.beforeRequired) { try { this.delegate.beforeCompletion(); } catch (RuntimeException rex) { // ignore } finally { this.beforeRequired = false; } } } public void afterCompletion(int status) { if (this.finishRequired) { try { this.delegate.afterCompletion(status); } catch (RuntimeException rex) { // ignore } finally { this.finishRequired = false; } } } public String toString() { return String.format("[%s] delegate: %s", this.getClass().getSimpleName(), this.delegate); } } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/internal/SynchronizationList.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.transaction.internal; import java.util.ArrayList; import java.util.List; import javax.transaction.Synchronization; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SynchronizationList implements Synchronization { private static final Logger logger = LoggerFactory.getLogger(SynchronizationList.class); private final List synchronizations = new ArrayList(); private boolean beforeCompletionInvoked; private boolean finishCompletionInvoked; public void registerSynchronizationQuietly(Synchronization sync) { SynchronizationImpl synchronization = new SynchronizationImpl(sync); this.synchronizations.add(synchronization); } public synchronized void beforeCompletion() { if (this.beforeCompletionInvoked == false) { int length = this.synchronizations.size(); for (int i = 0; i < length; i++) { Synchronization synchronization = this.synchronizations.get(i); try { synchronization.beforeCompletion(); } catch (RuntimeException error) { logger.error(error.getMessage(), error); } } // end-for this.beforeCompletionInvoked = true; } // end-if (this.beforeCompletionInvoked == false) } public synchronized void afterCompletion(int status) { if (this.finishCompletionInvoked == false) { int length = this.synchronizations.size(); for (int i = 0; i < length; i++) { Synchronization synchronization = this.synchronizations.get(i); try { synchronization.afterCompletion(status); } catch (RuntimeException error) { logger.error(error.getMessage(), error); } } // end-for this.finishCompletionInvoked = true; } // end-if (this.finishCompletionInvoked == false) } } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/internal/TransactionListenerList.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.transaction.internal; import java.util.ArrayList; import java.util.List; import org.bytesoft.transaction.supports.TransactionListener; import org.bytesoft.transaction.supports.TransactionListenerAdapter; import org.bytesoft.transaction.xa.TransactionXid; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class TransactionListenerList extends TransactionListenerAdapter { static final Logger logger = LoggerFactory.getLogger(TransactionListenerList.class); private final List listeners = new ArrayList(); public void registerTransactionListener(TransactionListener listener) { this.listeners.add(listener); } public void onPrepareStart(TransactionXid xid) { for (int i = 0; i < this.listeners.size(); i++) { try { TransactionListener listener = this.listeners.get(i); listener.onPrepareStart(xid); } catch (RuntimeException rex) { logger.error(rex.getMessage(), rex); } } } public void onPrepareSuccess(TransactionXid xid) { for (int i = 0; i < this.listeners.size(); i++) { try { TransactionListener listener = this.listeners.get(i); listener.onPrepareSuccess(xid); } catch (RuntimeException rex) { logger.error(rex.getMessage(), rex); } } } public void onPrepareFailure(TransactionXid xid) { for (int i = 0; i < this.listeners.size(); i++) { try { TransactionListener listener = this.listeners.get(i); listener.onPrepareFailure(xid); } catch (RuntimeException rex) { logger.error(rex.getMessage(), rex); } } } public void onCommitStart(TransactionXid xid) { for (int i = 0; i < this.listeners.size(); i++) { try { TransactionListener listener = this.listeners.get(i); listener.onCommitStart(xid); } catch (RuntimeException rex) { logger.error(rex.getMessage(), rex); } } } public void onCommitSuccess(TransactionXid xid) { for (int i = 0; i < this.listeners.size(); i++) { try { TransactionListener listener = this.listeners.get(i); listener.onCommitSuccess(xid); } catch (RuntimeException rex) { logger.error(rex.getMessage(), rex); } } } public void onCommitFailure(TransactionXid xid) { for (int i = 0; i < this.listeners.size(); i++) { try { TransactionListener listener = this.listeners.get(i); listener.onCommitFailure(xid); } catch (RuntimeException rex) { logger.error(rex.getMessage(), rex); } } } public void onCommitHeuristicMixed(TransactionXid xid) { for (int i = 0; i < this.listeners.size(); i++) { try { TransactionListener listener = this.listeners.get(i); listener.onCommitHeuristicMixed(xid); } catch (RuntimeException rex) { logger.error(rex.getMessage(), rex); } } } public void onCommitHeuristicRolledback(TransactionXid xid) { for (int i = 0; i < this.listeners.size(); i++) { try { TransactionListener listener = this.listeners.get(i); listener.onCommitHeuristicRolledback(xid); } catch (RuntimeException rex) { logger.error(rex.getMessage(), rex); } } } public void onRollbackStart(TransactionXid xid) { for (int i = 0; i < this.listeners.size(); i++) { try { TransactionListener listener = this.listeners.get(i); listener.onRollbackStart(xid); } catch (RuntimeException rex) { logger.error(rex.getMessage(), rex); } } } public void onRollbackSuccess(TransactionXid xid) { for (int i = 0; i < this.listeners.size(); i++) { try { TransactionListener listener = this.listeners.get(i); listener.onRollbackSuccess(xid); } catch (RuntimeException rex) { logger.error(rex.getMessage(), rex); } } } public void onRollbackFailure(TransactionXid xid) { for (int i = 0; i < this.listeners.size(); i++) { try { TransactionListener listener = this.listeners.get(i); listener.onRollbackFailure(xid); } catch (RuntimeException rex) { logger.error(rex.getMessage(), rex); } } } } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/internal/TransactionResourceListenerList.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.transaction.internal; import java.util.ArrayList; import java.util.List; import javax.transaction.xa.XAResource; import javax.transaction.xa.Xid; import org.bytesoft.transaction.supports.TransactionResourceListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class TransactionResourceListenerList implements TransactionResourceListener { static final Logger logger = LoggerFactory.getLogger(TransactionResourceListenerList.class); private final List listeners = new ArrayList(); public void registerTransactionResourceListener(TransactionResourceListener listener) { this.listeners.add(listener); } public void onEnlistResource(Xid xid, XAResource xares) { for (int i = 0; i < this.listeners.size(); i++) { try { TransactionResourceListener listener = this.listeners.get(i); listener.onEnlistResource(xid, xares); } catch (RuntimeException rex) { logger.error(rex.getMessage(), rex); } } } public void onDelistResource(Xid xid, XAResource xares) { for (int i = 0; i < this.listeners.size(); i++) { try { TransactionResourceListener listener = this.listeners.get(i); listener.onDelistResource(xid, xares); } catch (RuntimeException rex) { logger.error(rex.getMessage(), rex); } } } } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/logging/ArchiveDeserializer.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.transaction.logging; import org.bytesoft.transaction.xa.TransactionXid; public interface ArchiveDeserializer { public byte[] serialize(TransactionXid xid, Object archive); public Object deserialize(TransactionXid xid, byte[] array); } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/logging/LoggingFlushable.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.transaction.logging; public interface LoggingFlushable { public void flushImmediately(); } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/logging/TransactionLogger.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.transaction.logging; import org.bytesoft.transaction.archive.TransactionArchive; import org.bytesoft.transaction.archive.XAResourceArchive; import org.bytesoft.transaction.recovery.TransactionRecoveryCallback; public interface TransactionLogger { /* transaction */ public void createTransaction(TransactionArchive archive); public void updateTransaction(TransactionArchive archive); public void deleteTransaction(TransactionArchive archive); /* resource */ public void createResource(XAResourceArchive archive); public void updateResource(XAResourceArchive archive); public void deleteResource(XAResourceArchive archive); /* participant */ public void createParticipant(XAResourceArchive archive); public void updateParticipant(XAResourceArchive archive); public void deleteParticipant(XAResourceArchive archive); /* recovery */ public void recover(TransactionRecoveryCallback callback); } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/logging/store/VirtualLoggingKey.java ================================================ package org.bytesoft.transaction.logging.store; import java.util.Arrays; import javax.transaction.xa.Xid; public class VirtualLoggingKey implements Xid { public static final int FORMAT_ID = 9257; private byte[] globalTransactionId = new byte[0]; private byte[] branchQualifier = new byte[0]; public int hashCode() { int hash = 11; hash += 13 * this.getFormatId(); hash += 17 * Arrays.hashCode(branchQualifier); hash += 19 * Arrays.hashCode(globalTransactionId); return hash; } public boolean equals(Object obj) { if (obj == null) { return false; } else if (VirtualLoggingKey.class.isInstance(obj) == false) { return false; } VirtualLoggingKey that = (VirtualLoggingKey) obj; if (this.getFormatId() != that.getFormatId()) { return false; } else if (Arrays.equals(branchQualifier, that.branchQualifier) == false) { return false; } else if (Arrays.equals(globalTransactionId, that.globalTransactionId) == false) { return false; } return true; } public int getFormatId() { return FORMAT_ID; } public byte[] getGlobalTransactionId() { return globalTransactionId; } public void setGlobalTransactionId(byte[] globalTransactionId) { this.globalTransactionId = globalTransactionId; } public byte[] getBranchQualifier() { return branchQualifier; } public void setBranchQualifier(byte[] branchQualifier) { this.branchQualifier = branchQualifier; } } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/logging/store/VirtualLoggingListener.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.transaction.logging.store; public interface VirtualLoggingListener { public void recvOperation(VirtualLoggingRecord action); } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/logging/store/VirtualLoggingRecord.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.transaction.logging.store; import javax.transaction.xa.Xid; public class VirtualLoggingRecord { private Xid identifier; private int operator; private byte[] value; private byte[] content; public int getOperator() { return operator; } public void setOperator(int operator) { this.operator = operator; } public Xid getIdentifier() { return identifier; } public void setIdentifier(Xid identifier) { this.identifier = identifier; } public byte[] getValue() { return value; } public void setValue(byte[] value) { this.value = value; } public byte[] getContent() { return content; } public void setContent(byte[] content) { this.content = content; } } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/logging/store/VirtualLoggingSystem.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.transaction.logging.store; import javax.transaction.xa.Xid; public interface VirtualLoggingSystem { public static final int OPERATOR_CREATE = 1; public static final int OPERATOR_MOFIFY = 2; public static final int OPERATOR_DELETE = 3; public void create(Xid xid, byte[] byteArray); public void delete(Xid xid); public void modify(Xid xid, byte[] byteArray); public void traversal(VirtualLoggingListener listener); } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/logging/store/VirtualLoggingTrigger.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.transaction.logging.store; public interface VirtualLoggingTrigger { public void fireSwapImmediately(); } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/recovery/TransactionRecoveryCallback.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.transaction.recovery; import org.bytesoft.transaction.archive.TransactionArchive; public interface TransactionRecoveryCallback { public void recover(TransactionArchive archive); } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/recovery/TransactionRecoveryListener.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.transaction.recovery; import org.bytesoft.transaction.Transaction; public interface TransactionRecoveryListener { public void onRecovery(Transaction transaction); } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/remote/RemoteAddr.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.transaction.remote; import java.io.Serializable; import org.apache.commons.lang3.StringUtils; public class RemoteAddr implements Serializable { private static final long serialVersionUID = 1L; protected String serverHost; protected int serverPort; public int hashCode() { int hash = 7; hash += 11 * (this.serverHost == null ? 0 : this.serverHost.hashCode()); hash += 13 * this.serverPort; return hash; } public boolean equals(Object obj) { if (obj == null) { return false; } else if (RemoteAddr.class.isInstance(obj) == false) { return false; } RemoteAddr that = (RemoteAddr) obj; boolean hostEquals = StringUtils.equals(this.serverHost, that.serverHost); boolean portEquals = this.serverPort == that.serverPort; return hostEquals && portEquals; } public String toString() { return String.format("", this.serverHost, this.serverPort); } public String getServerHost() { return serverHost; } public void setServerHost(String serverHost) { this.serverHost = serverHost; } public int getServerPort() { return serverPort; } public void setServerPort(int serverPort) { this.serverPort = serverPort; } } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/remote/RemoteCoordinator.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.transaction.remote; import org.bytesoft.transaction.TransactionParticipant; public interface RemoteCoordinator extends TransactionParticipant { public String getIdentifier(); public RemoteAddr getRemoteAddr(); public RemoteNode getRemoteNode(); public String getApplication(); } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/remote/RemoteNode.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.transaction.remote; import org.apache.commons.lang3.StringUtils; public class RemoteNode extends RemoteAddr { private static final long serialVersionUID = 1L; protected String serviceKey; public int hashCode() { int hash = 7; hash += 11 * (this.serverHost == null ? 0 : this.serverHost.hashCode()); hash += 13 * this.serverPort; hash += 17 * (this.serviceKey == null ? 0 : this.serviceKey.hashCode()); return hash; } public boolean equals(Object obj) { if (obj == null) { return false; } else if (RemoteNode.class.isInstance(obj) == false) { return false; } RemoteNode that = (RemoteNode) obj; boolean hostEquals = StringUtils.equals(this.serverHost, that.serverHost); boolean portEquals = this.serverPort == that.serverPort; boolean servEquals = StringUtils.equals(this.serviceKey, that.serviceKey); return hostEquals && portEquals && servEquals; } public String toString() { return String.format("", this.serverHost, this.serviceKey, this.serverPort); } public String getServiceKey() { return serviceKey; } public void setServiceKey(String serviceKey) { this.serviceKey = serviceKey; } } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/remote/RemoteSvc.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.transaction.remote; import org.apache.commons.lang3.StringUtils; public class RemoteSvc extends RemoteAddr { private static final long serialVersionUID = 1L; protected String serviceKey; public int hashCode() { int hash = 19; hash += 23 * (this.serviceKey == null ? 0 : this.serviceKey.hashCode()); return hash; } public boolean equals(Object obj) { if (obj == null) { return false; } else if (RemoteSvc.class.isInstance(obj) == false) { return false; } RemoteSvc that = (RemoteSvc) obj; return StringUtils.equals(this.serviceKey, that.serviceKey); } public String toString() { return String.format("", this.serviceKey); } public String getServiceKey() { return serviceKey; } public void setServiceKey(String serviceKey) { this.serviceKey = serviceKey; } } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/resource/XATerminator.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.transaction.resource; import java.util.List; import org.bytesoft.transaction.archive.XAResourceArchive; public interface XATerminator extends javax.transaction.xa.XAResource { public List getResourceArchives(); } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/supports/TransactionExtra.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.transaction.supports; import org.bytesoft.transaction.xa.TransactionXid; public interface TransactionExtra { public TransactionXid getTransactionXid(); } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/supports/TransactionListener.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.transaction.supports; import org.bytesoft.transaction.xa.TransactionXid; public interface TransactionListener { public void onPrepareStart(TransactionXid xid); public void onPrepareSuccess(TransactionXid xid); public void onPrepareFailure(TransactionXid xid); public void onCommitStart(TransactionXid xid); public void onCommitSuccess(TransactionXid xid); public void onCommitFailure(TransactionXid xid); public void onCommitHeuristicMixed(TransactionXid xid); public void onCommitHeuristicRolledback(TransactionXid xid); public void onRollbackStart(TransactionXid xid); public void onRollbackSuccess(TransactionXid xid); public void onRollbackFailure(TransactionXid xid); } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/supports/TransactionListenerAdapter.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.transaction.supports; import org.bytesoft.transaction.xa.TransactionXid; public class TransactionListenerAdapter implements TransactionListener { public void onPrepareStart(TransactionXid xid) { } public void onPrepareSuccess(TransactionXid xid) { } public void onPrepareFailure(TransactionXid xid) { } public void onCommitStart(TransactionXid xid) { } public void onCommitSuccess(TransactionXid xid) { } public void onCommitFailure(TransactionXid xid) { } public void onCommitHeuristicMixed(TransactionXid xid) { } public void onCommitHeuristicRolledback(TransactionXid xid) { } public void onRollbackStart(TransactionXid xid) { } public void onRollbackSuccess(TransactionXid xid) { } public void onRollbackFailure(TransactionXid xid) { } } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/supports/TransactionResourceListener.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.transaction.supports; import javax.transaction.xa.XAResource; import javax.transaction.xa.Xid; public interface TransactionResourceListener { public void onEnlistResource(Xid xid, XAResource xares); public void onDelistResource(Xid xid, XAResource xares); } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/supports/TransactionResourceListenerAdapter.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.transaction.supports; import javax.transaction.xa.XAResource; import javax.transaction.xa.Xid; public class TransactionResourceListenerAdapter implements TransactionResourceListener { public void onEnlistResource(Xid xid, XAResource xares) { } public void onDelistResource(Xid xid, XAResource xares) { } } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/supports/TransactionStatistic.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.transaction.supports; import org.bytesoft.bytejta.TransactionImpl; public interface TransactionStatistic { public static final long FLAGS_ACTIVE = 0x1; public static final long FLAGS_PREPARING = 0x2; public static final long FLAGS_PREPARED = 0x4; public static final long FLAGS_COMMITTING = 0x8; public static final long FLAGS_COMMITTED = 0x10; public static final long FLAGS_ROLLINGBACK = 0x20; public static final long FLAGS_ROLEDBACK = 0x40; public static final long FLAGS_ERROR = 0x80; public static final long FLAGS_ERROR_TOTAL = 0x100; public void fireBeginTransaction(TransactionImpl transaction); public void firePreparingTransaction(TransactionImpl transaction); public void firePreparedTransaction(TransactionImpl transaction); public void fireCommittingTransaction(TransactionImpl transaction); public void fireCommittedTransaction(TransactionImpl transaction); public void fireRollingBackTransaction(TransactionImpl transaction); public void fireRolledbackTransaction(TransactionImpl transaction); public void fireCompleteFailure(TransactionImpl transaction); public void fireCleanupTransaction(TransactionImpl transaction); public void fireRecoverTransaction(TransactionImpl transaction); } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/supports/TransactionTimer.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.transaction.supports; import org.bytesoft.transaction.Transaction; public interface TransactionTimer { public void timingExecution(); public void stopTiming(Transaction tx); } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/supports/resource/XAResourceDescriptor.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.transaction.supports.resource; import javax.transaction.xa.XAResource; import javax.transaction.xa.Xid; public interface XAResourceDescriptor extends XAResource { public void setIdentifier(String identifier); public String getIdentifier(); public XAResource getDelegate(); public void setTransactionTimeoutQuietly(int timeout); public boolean isTransactionCommitted(Xid xid) throws IllegalStateException; } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/supports/rpc/TransactionInterceptor.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.transaction.supports.rpc; public interface TransactionInterceptor { public void beforeSendRequest(TransactionRequest request) throws IllegalStateException; public void beforeSendResponse(TransactionResponse response) throws IllegalStateException; public void afterReceiveRequest(TransactionRequest request) throws IllegalStateException; public void afterReceiveResponse(TransactionResponse response) throws IllegalStateException; } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/supports/rpc/TransactionRequest.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.transaction.supports.rpc; import org.bytesoft.transaction.TransactionContext; import org.bytesoft.transaction.remote.RemoteCoordinator; public interface TransactionRequest { public RemoteCoordinator getTargetTransactionCoordinator(); public TransactionContext getTransactionContext(); public void setTransactionContext(TransactionContext transactionContext); public Object getHeader(String name); public void setHeader(String name, Object value); } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/supports/rpc/TransactionResponse.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.transaction.supports.rpc; import org.bytesoft.transaction.TransactionContext; import org.bytesoft.transaction.remote.RemoteCoordinator; public interface TransactionResponse { public boolean isParticipantStickyRequired(); public boolean isParticipantRollbackOnly(); public RemoteCoordinator getSourceTransactionCoordinator(); public TransactionContext getTransactionContext(); public void setTransactionContext(TransactionContext transactionContext); public Object getHeader(String name); public void setHeader(String name, Object value); } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/supports/serialize/XAResourceDeserializer.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.transaction.supports.serialize; import org.bytesoft.transaction.supports.resource.XAResourceDescriptor; public interface XAResourceDeserializer { public XAResourceDescriptor deserialize(String identifier); } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/work/SimpleWork.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.transaction.work; import javax.resource.spi.work.Work; import javax.resource.spi.work.WorkEvent; import javax.resource.spi.work.WorkListener; public class SimpleWork implements Runnable { private Object source; private Work work; private WorkListener workListener; public void run() { this.workListener.workStarted(new WorkEvent(this.source, WorkEvent.WORK_STARTED, this.work, null)); this.work.run(); this.workListener.workCompleted(new WorkEvent(this.source, WorkEvent.WORK_COMPLETED, this.work, null)); } public void setSource(Object source) { this.source = source; } public void setWork(Work work) { this.work = work; } public void setWorkListener(WorkListener workListener) { this.workListener = workListener; } } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/work/SimpleWorkListener.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.transaction.work; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import javax.resource.spi.work.WorkEvent; import javax.resource.spi.work.WorkListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SimpleWorkListener implements WorkListener { static final Logger logger = LoggerFactory.getLogger(SimpleWorkListener.class); private long acceptedTime = -1; private long startedTime = -1; private final WorkListener delegate; private final Lock lock = new ReentrantLock(); private final Condition condition = this.lock.newCondition(); public SimpleWorkListener(WorkListener workListener) { this.delegate = workListener; } public long waitForStart() { try { this.lock.lock(); while (this.acceptedTime < 0 || this.startedTime < 0) { try { this.condition.await(); } catch (InterruptedException ex) { logger.debug(ex.getMessage()); } } return this.startedTime - this.acceptedTime; } finally { this.lock.unlock(); } } public void signalStarted() { try { this.lock.lock(); this.condition.signalAll(); } finally { this.lock.unlock(); } } public void workAccepted(WorkEvent event) { if (this.delegate != null) { delegate.workAccepted(event); } this.acceptedTime = System.currentTimeMillis(); } public void workCompleted(WorkEvent event) { if (this.delegate != null) { delegate.workCompleted(event); } } public void workRejected(WorkEvent event) { if (this.delegate != null) { delegate.workRejected(event); } } public void workStarted(WorkEvent event) { if (this.delegate != null) { delegate.workStarted(event); } this.startedTime = System.currentTimeMillis(); this.signalStarted(); } } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/work/SimpleWorkManager.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.transaction.work; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import javax.resource.spi.work.ExecutionContext; import javax.resource.spi.work.Work; import javax.resource.spi.work.WorkEvent; import javax.resource.spi.work.WorkException; import javax.resource.spi.work.WorkListener; import javax.resource.spi.work.WorkManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SimpleWorkManager implements WorkManager { static final Logger logger = LoggerFactory.getLogger(SimpleWorkManager.class); private final ThreadPoolExecutor executor = new ThreadPoolExecutor(5, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue()); // private final ScheduledThreadPoolExecutor scheduled = new ScheduledThreadPoolExecutor(1); public void doWork(Work work) throws WorkException { this.doWork(work, 1800 * 1000L, null, null); } public void doWork(Work work, long startTimeout, ExecutionContext execContext, WorkListener workListener) throws WorkException { SimpleWorkListener wrappedListener = new SimpleWorkListener(workListener); wrappedListener.workAccepted(new WorkEvent(this, WorkEvent.WORK_ACCEPTED, work, null)); SimpleWork task = new SimpleWork(); task.setWork(work); task.setWorkListener(wrappedListener); Future future = this.executor.submit(task); try { future.get(); } catch (CancellationException ex) { wrappedListener.workCompleted(new WorkEvent(this, WorkEvent.WORK_REJECTED, work, new WorkException(ex))); } catch (InterruptedException ex) { wrappedListener.workCompleted(new WorkEvent(this, WorkEvent.WORK_COMPLETED, work, new WorkException(ex))); } catch (ExecutionException ex) { wrappedListener.workCompleted(new WorkEvent(this, WorkEvent.WORK_COMPLETED, work, new WorkException(ex))); } } public long startWork(Work work) throws WorkException { return this.startWork(work, 1800 * 1000L, null, null); } public long startWork(Work work, long startTimeout, ExecutionContext execContext, WorkListener workListener) throws WorkException { SimpleWorkListener wrappedListener = new SimpleWorkListener(workListener); wrappedListener.workAccepted(new WorkEvent(this, WorkEvent.WORK_ACCEPTED, work, null)); SimpleWork task = new SimpleWork(); task.setSource(this); task.setWork(work); task.setWorkListener(wrappedListener); this.executor.submit(task); return wrappedListener.waitForStart(); } public void scheduleWork(Work work) throws WorkException { throw new WorkException("not supported yet!"); } public void scheduleWork(Work work, long startTimeout, ExecutionContext execContext, WorkListener workListener) throws WorkException { // SimpleWorkListener wrappedListener = new SimpleWorkListener(workListener); // wrappedListener.workAccepted(new WorkEvent(this, WorkEvent.WORK_ACCEPTED, work, null)); // SimpleWork task = new SimpleWork(); // task.setSource(this); // task.setWork(work); // task.setWorkListener(wrappedListener); // // ScheduledFuture future = // this.scheduled.scheduleAtFixedRate(task, 0, 1000, TimeUnit.MILLISECONDS); throw new WorkException("not supported yet!"); } } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/xa/TransactionXid.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.transaction.xa; import java.io.Serializable; import java.util.Arrays; import javax.transaction.xa.Xid; import org.bytesoft.common.utils.ByteUtils; public class TransactionXid implements Xid, Cloneable, Serializable { private static final long serialVersionUID = 1L; private int formatId; private byte[] globalTransactionId; private byte[] branchQualifier; public TransactionXid() { } public TransactionXid(int formatId, byte[] global) { this(formatId, global, new byte[0]); } public TransactionXid(int formatId, byte[] global, byte[] branch) { if (global == null) { throw new IllegalArgumentException("globalTransactionId cannot be null."); } else if (global.length > MAXGTRIDSIZE) { throw new IllegalArgumentException("length of globalTransactionId cannot exceed 64 bytes."); } if (branch == null) { throw new IllegalArgumentException("branchQualifier cannot be null."); } else if (branch.length > MAXBQUALSIZE) { throw new IllegalArgumentException("length of branchQualifier cannot exceed 64 bytes."); } this.globalTransactionId = new byte[global.length]; this.branchQualifier = new byte[branch.length]; System.arraycopy(global, 0, this.globalTransactionId, 0, global.length); System.arraycopy(branch, 0, this.branchQualifier, 0, branch.length); this.formatId = formatId; this.globalTransactionId = global; this.branchQualifier = branch; } public TransactionXid clone() { TransactionXid that = new TransactionXid(); that.setFormatId(this.formatId); byte[] global = new byte[this.globalTransactionId.length]; byte[] branch = new byte[this.branchQualifier.length]; System.arraycopy(this.globalTransactionId, 0, global, 0, this.globalTransactionId.length); System.arraycopy(this.branchQualifier, 0, branch, 0, this.branchQualifier.length); that.setGlobalTransactionId(global); that.setBranchQualifier(branch); return that; } public int getFormatId() { return this.formatId; } public int hashCode() { int hash = 23; hash += 29 * this.getFormatId(); hash += 31 * Arrays.hashCode(branchQualifier); hash += 37 * Arrays.hashCode(globalTransactionId); return hash; } public boolean equals(Object obj) { if (this == obj) { return true; } else if (obj == null) { return false; } else if (getClass() != obj.getClass()) { return false; } TransactionXid other = (TransactionXid) obj; if (this.formatId != other.formatId) { return false; } else if (Arrays.equals(branchQualifier, other.branchQualifier) == false) { return false; } else if (Arrays.equals(globalTransactionId, other.globalTransactionId) == false) { return false; } return true; } public String toString() { String global = this.globalTransactionId == null ? null : ByteUtils.byteArrayToString(this.globalTransactionId); String branch = this.branchQualifier == null ? null : ByteUtils.byteArrayToString(this.branchQualifier); return String.format("%s-%s-%s", this.getFormatId(), global, branch); } public byte[] getGlobalTransactionId() { return globalTransactionId; } public void setGlobalTransactionId(byte[] globalTransactionId) { this.globalTransactionId = globalTransactionId; } public byte[] getBranchQualifier() { return branchQualifier; } public void setBranchQualifier(byte[] branchQualifier) { this.branchQualifier = branchQualifier; } public void setFormatId(int formatId) { this.formatId = formatId; } } ================================================ FILE: bytejta-core/src/main/java/org/bytesoft/transaction/xa/XidFactory.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.transaction.xa; public interface XidFactory { public static final int JTA_FORMAT_ID = 1207; public static final int TCC_FORMAT_ID = 8127; public static final int GLOBAL_TRANSACTION_LENGTH = 16; public static final int BRANCH_QUALIFIER_LENGTH = 16; public TransactionXid createGlobalXid(); public TransactionXid createGlobalXid(byte[] globalTransactionId); public TransactionXid createBranchXid(TransactionXid globalXid); public TransactionXid createBranchXid(TransactionXid globalXid, byte[] branchQualifier); } ================================================ FILE: bytejta-supports/pom.xml ================================================ 4.0.0 org.bytesoft bytejta-parent 0.5.0-BETA9 bytejta-supports jar bytejta-supports The bytejta-supports project is the module of ByteJTA for integrating with third-party open source project. http://www.bytesoft.org UTF-8 org.bytesoft bytejta-core org.springframework spring-context org.springframework spring-tx org.springframework spring-jdbc org.springframework spring-aop aopalliance aopalliance cglib cglib asm asm org.ow2.asm asm org.aspectj aspectjweaver org.apache.zookeeper zookeeper org.apache.curator curator-recipes org.springframework.boot spring-boot org.springframework.boot spring-boot-autoconfigure org.apache.commons commons-dbcp2 org.hibernate hibernate-core ================================================ FILE: bytejta-supports/src/main/java/org/bytesoft/bytejta/supports/boot/jdbc/DataSourceCciBuilder.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.bytejta.supports.boot.jdbc; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.util.HashMap; import java.util.Map; import javax.sql.DataSource; import javax.sql.XADataSource; import org.apache.commons.dbcp2.managed.BasicManagedDataSource; import org.apache.commons.lang3.StringUtils; import org.bytesoft.bytejta.supports.resource.ManagedConnectionFactoryHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.BeanNameAware; import org.springframework.boot.context.properties.bind.Bindable; import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.context.properties.source.ConfigurationPropertyName; import org.springframework.boot.context.properties.source.ConfigurationPropertyNameAliases; import org.springframework.boot.context.properties.source.ConfigurationPropertySource; import org.springframework.boot.context.properties.source.MapConfigurationPropertySource; public class DataSourceCciBuilder { static final Logger logger = LoggerFactory.getLogger(DataSourceCciBuilder.class); private XADataSource xaDataSourceInstance; private final Map properties = new HashMap(); private DataSourceCciBuilder(XADataSource xaDataSourceInstance) { if (xaDataSourceInstance == null) { throw new IllegalArgumentException("the xaDataSourceInstance cannot be null!"); } this.xaDataSourceInstance = xaDataSourceInstance; } @SuppressWarnings("rawtypes") public static DataSourceCciBuilder create(XADataSource xaDataSourceInstance) { return new DataSourceCciBuilder(xaDataSourceInstance); } @SuppressWarnings("unchecked") public DataSource build() { this.validateXADataSourceInstance(); BasicManagedDataSource dataSource = BeanUtils.instantiateClass(BasicManagedDataSource.class); this.bind(dataSource); dataSource.setXaDataSourceInstance(this.xaDataSourceInstance); return (T) dataSource; } private void validateXADataSourceInstance() { InvocationHandler handler = Proxy.isProxyClass(this.xaDataSourceInstance.getClass()) ? Proxy.getInvocationHandler(this.xaDataSourceInstance) : null; ManagedConnectionFactoryHandler mcfh = // (handler == null || ManagedConnectionFactoryHandler.class.isInstance(handler) == false) ? null : (ManagedConnectionFactoryHandler) handler; if (BeanNameAware.class.isInstance(this.xaDataSourceInstance) == false && (mcfh == null || StringUtils.isBlank(mcfh.getIdentifier()))) { throw new IllegalStateException("XADataSource is not properly configured!"); } } private void bind(DataSource dataSource) { ConfigurationPropertySource source = new MapConfigurationPropertySource(this.properties); ConfigurationPropertyNameAliases aliases = new ConfigurationPropertyNameAliases(); Binder binder = new Binder(source.withAliases(aliases)); binder.bind(ConfigurationPropertyName.EMPTY, Bindable.ofInstance(dataSource)); } } ================================================ FILE: bytejta-supports/src/main/java/org/bytesoft/bytejta/supports/boot/jdbc/DataSourceSpiBuilder.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.bytejta.supports.boot.jdbc; import java.util.HashMap; import java.util.Map; import javax.sql.XADataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeanUtils; import org.springframework.boot.context.properties.bind.Bindable; import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.context.properties.source.ConfigurationPropertyName; import org.springframework.boot.context.properties.source.ConfigurationPropertyNameAliases; import org.springframework.boot.context.properties.source.ConfigurationPropertySource; import org.springframework.boot.context.properties.source.MapConfigurationPropertySource; import org.springframework.util.ClassUtils; public class DataSourceSpiBuilder { private static final String[] XA_DATA_SOURCE_TYPE_NAMES = new String[] { "com.mysql.jdbc.jdbc2.optional.MysqlXADataSource", "org.postgresql.xa.PGXADataSource", "oracle.jdbc.xa.client.OracleXADataSource" }; static final Logger logger = LoggerFactory.getLogger(DataSourceSpiBuilder.class); private Class type; private final Map properties = new HashMap(); private ClassLoader classLoader; private DataSourceSpiBuilder(ClassLoader classLoader) { this.classLoader = classLoader; } @SuppressWarnings("rawtypes") public static DataSourceSpiBuilder create() { return new DataSourceSpiBuilder(null); } @SuppressWarnings("rawtypes") public static DataSourceSpiBuilder create(ClassLoader classLoader) { return new DataSourceSpiBuilder(classLoader); } @SuppressWarnings("deprecation") public XADataSource build() { Class type = this.getType(); XADataSource xaDataSourceInstance = BeanUtils.instantiate(type); this.bind(xaDataSourceInstance); return xaDataSourceInstance; } private void bind(XADataSource result) { ConfigurationPropertySource source = new MapConfigurationPropertySource(this.properties); ConfigurationPropertyNameAliases aliases = new ConfigurationPropertyNameAliases(); aliases.addAliases("url", "jdbc-url"); aliases.addAliases("username", "user"); Binder binder = new Binder(source.withAliases(aliases)); binder.bind(ConfigurationPropertyName.EMPTY, Bindable.ofInstance(result)); } public DataSourceSpiBuilder type(Class type) { this.type = type; return this; } public DataSourceSpiBuilder url(String url) { this.properties.put("url", url); return this; } public DataSourceSpiBuilder username(String username) { this.properties.put("username", username); return this; } public DataSourceSpiBuilder password(String password) { this.properties.put("password", password); return this; } private Class getType() { Class type = (this.type != null) ? this.type : findType(this.classLoader); if (type != null) { return type; } else { throw new IllegalStateException("No supported XADataSource type found"); } } @SuppressWarnings("unchecked") public static Class findType(ClassLoader classLoader) { for (String name : XA_DATA_SOURCE_TYPE_NAMES) { try { return (Class) ClassUtils.forName(name, classLoader); } catch (Exception error) { logger.debug("Error occurred while loading class: {}!", name, error); } } return null; } } ================================================ FILE: bytejta-supports/src/main/java/org/bytesoft/bytejta/supports/internal/RemoteCoordinatorRegistry.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.internal; import java.util.Arrays; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; 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; public class RemoteCoordinatorRegistry { static final RemoteCoordinatorRegistry instance = new RemoteCoordinatorRegistry(); /* host:port -> participant(url= host: port) */ private final Map physicalMap = new ConcurrentHashMap(); /* host:port -> host:application:port */ private final Map mappings = new ConcurrentHashMap(); /* application -> participant */ private final Map clusterMap = new ConcurrentHashMap(); /* invocation -> host:application:port */ private final Map invocationMap = new ConcurrentHashMap(); public void putInvocationDef(InvocationDef invocationDef, RemoteNode remoteNode) { this.invocationMap.put(invocationDef, remoteNode); } public RemoteNode getRemoteNodeByInvocationDef(InvocationDef invocationDef) { return this.invocationMap.get(invocationDef); } public boolean containsInvocationDef(InvocationDef invocationDef) { return this.invocationMap.containsKey(invocationDef); } public void removetInvocationDef(InvocationDef invocationDef) { this.invocationMap.remove(invocationDef); } public void putPhysicalInstance(RemoteAddr remoteAddr, RemoteCoordinator participant) { this.physicalMap.put(remoteAddr, participant); } public RemoteCoordinator getPhysicalInstance(RemoteAddr remoteAddr) { return this.physicalMap.get(remoteAddr); } public boolean containsPhysicalInstance(RemoteAddr remoteAddr) { return this.physicalMap.containsKey(remoteAddr); } public void removePhysicalInstance(RemoteAddr remoteAddr) { this.physicalMap.remove(remoteAddr); } public void putRemoteNode(RemoteAddr remoteAddr, RemoteNode remoteNode) { this.mappings.put(remoteAddr, remoteNode); } public RemoteNode getRemoteNode(RemoteAddr remoteAddr) { return this.mappings.get(remoteAddr); } public boolean containsRemoteNode(RemoteAddr remoteAddr) { return this.mappings.containsKey(remoteAddr); } public void removeRemoteNode(RemoteAddr remoteAddr) { this.mappings.remove(remoteAddr); } public void putParticipant(String application, RemoteCoordinator participant) { this.clusterMap.put(application, participant); } public boolean containsParticipant(String application) { return this.clusterMap.containsKey(application); } public RemoteCoordinator getParticipant(String application) { return this.clusterMap.get(application); } public void removeParticipant(String application) { this.clusterMap.remove(application); } private RemoteCoordinatorRegistry() { if (instance != null) { throw new IllegalStateException(); } } public static RemoteCoordinatorRegistry getInstance() { return instance; } public static class InvocationDef { private Class interfaceClass; private String methodName; private Class[] parameterTypes; public int hashCode() { int hash = 3; hash += 5 * this.interfaceClass.hashCode(); hash += 7 * this.methodName.hashCode(); hash += 11 * Arrays.hashCode(this.parameterTypes); return hash; } public boolean equals(Object obj) { if (obj == null) { return false; } else if (InvocationDef.class.isInstance(obj) == false) { return false; } InvocationDef that = (InvocationDef) obj; boolean clazzEquals = CommonUtils.equals(this.interfaceClass, that.interfaceClass); boolean methodEquals = StringUtils.equals(this.methodName, that.methodName); boolean typesEquals = Arrays.equals(this.parameterTypes, that.parameterTypes); return clazzEquals && methodEquals && typesEquals; } public Class getInterfaceClass() { return interfaceClass; } public void setInterfaceClass(Class interfaceClass) { this.interfaceClass = interfaceClass; } public String getMethodName() { return methodName; } public void setMethodName(String methodName) { this.methodName = methodName; } public Class[] getParameterTypes() { return parameterTypes; } public void setParameterTypes(Class[] parameterTypes) { this.parameterTypes = parameterTypes; } } } ================================================ FILE: bytejta-supports/src/main/java/org/bytesoft/bytejta/supports/internal/TransactionCommandDispatcher.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.bytejta.supports.internal; import java.util.concurrent.Callable; 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.resource.spi.work.WorkManager; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.recipes.leader.LeaderSelector; import org.apache.curator.framework.recipes.leader.LeaderSelectorListener; import org.apache.curator.framework.state.ConnectionState; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException.NodeExistsException; import org.bytesoft.common.utils.CommonUtils; import org.bytesoft.transaction.aware.TransactionEndpointAware; import org.bytesoft.transaction.cmd.CommandDispatcher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.SmartInitializingSingleton; public class TransactionCommandDispatcher implements SmartInitializingSingleton, TransactionEndpointAware, LeaderSelectorListener, CommandDispatcher { static final Logger logger = LoggerFactory.getLogger(TransactionCommandDispatcher.class); @javax.annotation.Resource private CuratorFramework curatorFramework; private Lock stateLock = new ReentrantLock(); private Condition stateCondition = this.stateLock.newCondition(); private volatile Boolean stateDisallowed; private volatile boolean permsDisallowed; private String endpoint; private LeaderSelector leadSelector; @javax.annotation.Resource private WorkManager workManager; private String workDirectory = "/org/bytesoft/bytejta"; public void dispatch(Runnable runnable) throws Exception { if (this.hasLeadership() == false) { throw new SecurityException("Current node is not the master!"); } this.checkExecutionPermission(); WorkImpl work = new WorkImpl(runnable); this.workManager.startWork(work); } public Object dispatch(Callable callable) throws Exception { if (this.hasLeadership() == false) { throw new SecurityException("Current node is not the master!"); } this.checkExecutionPermission(); WorkImpl work = new WorkImpl(callable); this.workManager.startWork(work); return work.waitForResultIfNecessary(); } private void checkExecutionPermission() { if (this.stateDisallowed != null && this.stateDisallowed) { throw new SecurityException("Current state is no longer connected!"); } else if (this.permsDisallowed) { throw new SecurityException("Current node is no longer the master!"); } } public void takeLeadership(CuratorFramework client) throws Exception { try { this.stateLock.lock(); this.permsDisallowed = false; if (this.stateDisallowed != null && this.stateDisallowed) { logger.warn("Wrong state! Re-elect the master node."); } else { this.stateCondition.awaitUninterruptibly(); } } finally { this.permsDisallowed = true; this.stateLock.unlock(); } } public boolean hasLeadership() { if (this.leadSelector == null) { return false; } else { return this.leadSelector.hasLeadership(); } } public void stateChanged(CuratorFramework client, ConnectionState newState) { try { this.stateLock.lock(); this.stateDisallowed = ConnectionState.CONNECTED.equals(newState) == false && ConnectionState.RECONNECTED.equals(newState) == false; if (this.stateDisallowed) { this.stateCondition.signalAll(); } // end-if (this.stateDisallowed) } finally { this.stateLock.unlock(); } } public void afterSingletonsInstantiated() { String basePath = String.format("%s/%s", this.workDirectory, this.getApplication()); try { this.createPersistentPathIfNecessary(basePath); } catch (Exception ex) { throw new IllegalStateException(ex); } String masterPath = String.format("%s/master", basePath); this.leadSelector = new LeaderSelector(this.curatorFramework, masterPath, this); this.leadSelector.autoRequeue(); this.leadSelector.start(); } private void createPersistentPathIfNecessary(String path) throws Exception { try { this.curatorFramework.create() // .creatingParentContainersIfNeeded().withMode(CreateMode.PERSISTENT).forPath(path); } catch (NodeExistsException nex) { logger.debug("Path exists(path= {})!", path); } } public String getWorkDirectory() { return workDirectory; } public void setWorkDirectory(String workDirectory) { this.workDirectory = workDirectory; } class WorkImpl implements Work { private final Lock lock = new ReentrantLock(); private final Condition condition = this.lock.newCondition(); private final Callable callable; private final Runnable runnable; private Object result; private Boolean error; public WorkImpl(Callable callable) { this.runnable = null; this.callable = callable; } public WorkImpl(Runnable runnable) { this.callable = null; this.runnable = runnable; } public Object waitForResultIfNecessary() throws Exception { if (this.callable == null) { return null; } else { return this.waitForResult(); } } public Object waitForResult() throws Exception { try { this.lock.lock(); if (this.error == null) { this.condition.awaitUninterruptibly(); } if (this.error == false) { return this.result; } else if (Exception.class.isInstance(this.result)) { throw (Exception) this.result; } else { throw new RuntimeException((Throwable) this.result); } } finally { this.lock.unlock(); } } public void run() { if (this.callable != null) { this.executeCallable(); } else if (this.runnable != null) { this.executeRunnable(); } } private void executeCallable() { try { this.lock.lock(); checkExecutionPermission(); this.result = this.callable.call(); this.error = false; this.condition.signalAll(); } catch (Exception error) { this.result = error; this.error = true; this.condition.signalAll(); } finally { this.lock.unlock(); } } private void executeRunnable() { try { this.runnable.run(); } catch (Exception ex) { logger.error("Error occurred while executing task(task= {}).", this.runnable); } } public void release() { } public Object getResult() { return result; } public void setResult(Object result) { this.result = result; } public Boolean getError() { return error; } public void setError(Boolean error) { this.error = error; } } public CuratorFramework getCuratorFramework() { return curatorFramework; } public void setCuratorFramework(CuratorFramework curatorFramework) { this.curatorFramework = curatorFramework; } private String getApplication() { return CommonUtils.getApplication(this.endpoint); } public String getEndpoint() { return this.endpoint; } public void setEndpoint(String identifier) { this.endpoint = identifier; } } ================================================ FILE: bytejta-supports/src/main/java/org/bytesoft/bytejta/supports/jdbc/LocalXADataSource.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.io.PrintWriter; import java.sql.Connection; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.util.logging.Logger; import javax.sql.DataSource; import javax.sql.XADataSource; import javax.transaction.RollbackException; import javax.transaction.SystemException; import javax.transaction.TransactionManager; import org.bytesoft.transaction.Transaction; import org.bytesoft.transaction.TransactionContext; import org.bytesoft.transaction.supports.resource.XAResourceDescriptor; import org.springframework.beans.factory.BeanNameAware; public class LocalXADataSource /* extends TransactionListenerAdapter */ implements XADataSource, DataSource, DataSourceHolder, BeanNameAware { private PrintWriter logWriter; private int loginTimeout; private DataSource dataSource; private String beanName; @javax.annotation.Resource private TransactionManager transactionManager; public Connection getConnection() throws SQLException { try { Transaction transaction = (Transaction) this.transactionManager.getTransaction(); if (transaction == null) { return this.dataSource.getConnection(); } XAResourceDescriptor descriptor = transaction.getResourceDescriptor(this.beanName); LocalXAResource resource = descriptor == null ? null : (LocalXAResource) descriptor.getDelegate(); LocalXAConnection xacon = resource == null ? null : resource.getManagedConnection(); if (xacon != null) { return xacon.getConnection(); } Transaction transactionExtra = (Transaction) transaction.getTransactionalExtra(); TransactionContext transactionContext = transaction.getTransactionContext(); TransactionContext extraContext = transactionExtra != null ? transactionExtra.getTransactionContext() : null; boolean transactionCompatibleLoggingLRO = LocalXACompatible.class.isInstance(transactionContext) // ? ((LocalXACompatible) transactionContext).compatibleLoggingLRO() : false; boolean extraCompatibleLoggingLRO = extraContext != null && LocalXACompatible.class.isInstance(extraContext) // ? ((LocalXACompatible) extraContext).compatibleLoggingLRO() : false; boolean loggingRequired = transactionCompatibleLoggingLRO || extraCompatibleLoggingLRO; xacon = this.getXAConnection(); LogicalConnection connection = xacon.getConnection(); descriptor = xacon.getXAResource(loggingRequired); transaction.enlistResource(descriptor); return connection; } catch (SystemException ex) { throw new SQLException(ex); } catch (RollbackException ex) { throw new SQLException(ex); } catch (RuntimeException ex) { throw new SQLException(ex); } } public Connection getConnection(String username, String password) throws SQLException { try { Transaction transaction = (Transaction) this.transactionManager.getTransaction(); if (transaction == null) { return this.dataSource.getConnection(username, password); } XAResourceDescriptor descriptor = transaction.getResourceDescriptor(this.beanName); LocalXAResource resource = descriptor == null ? null : (LocalXAResource) descriptor.getDelegate(); LocalXAConnection xacon = resource == null ? null : resource.getManagedConnection(); if (xacon != null) { return xacon.getConnection(); } Transaction transactionExtra = (Transaction) transaction.getTransactionalExtra(); TransactionContext transactionContext = transaction.getTransactionContext(); TransactionContext extraContext = transactionExtra != null ? transactionExtra.getTransactionContext() : null; boolean transactionCompatibleLoggingLRO = LocalXACompatible.class.isInstance(transactionContext) // ? ((LocalXACompatible) transactionContext).compatibleLoggingLRO() : false; boolean extraCompatibleLoggingLRO = extraContext != null && LocalXACompatible.class.isInstance(extraContext) // ? ((LocalXACompatible) extraContext).compatibleLoggingLRO() : false; boolean loggingRequired = transactionCompatibleLoggingLRO || extraCompatibleLoggingLRO; xacon = this.getXAConnection(username, password); LogicalConnection connection = xacon.getConnection(); descriptor = xacon.getXAResource(loggingRequired); transaction.enlistResource(descriptor); return connection; } catch (SystemException ex) { throw new SQLException(ex); } catch (RollbackException ex) { throw new SQLException(ex); } catch (RuntimeException ex) { throw new SQLException(ex); } } public boolean isWrapperFor(Class iface) { if (iface == null) { return false; } else if (iface.isInstance(this)) { return true; } return false; } @SuppressWarnings("unchecked") public T unwrap(Class iface) { if (iface == null) { return null; } else if (iface.isInstance(this)) { return (T) this; } return null; } public LocalXAConnection getXAConnection() throws SQLException { Connection connection = this.dataSource.getConnection(); LocalXAConnection xacon = new LocalXAConnection(connection); xacon.setResourceId(this.beanName); return xacon; } public LocalXAConnection getXAConnection(String user, String passwd) throws SQLException { Connection connection = this.dataSource.getConnection(user, passwd); LocalXAConnection xacon = new LocalXAConnection(connection); xacon.setResourceId(this.beanName); return xacon; } public Logger getParentLogger() throws SQLFeatureNotSupportedException { throw new SQLFeatureNotSupportedException(); } public void setBeanName(String name) { this.beanName = name; } public PrintWriter getLogWriter() { return logWriter; } public void setLogWriter(PrintWriter logWriter) { this.logWriter = logWriter; } public int getLoginTimeout() { return loginTimeout; } public void setLoginTimeout(int loginTimeout) { this.loginTimeout = loginTimeout; } public DataSource getDataSource() { return dataSource; } public void setDataSource(DataSource dataSource) { if (LocalXADataSource.class.isInstance(dataSource)) { LocalXADataSource that = (LocalXADataSource) dataSource; this.dataSource = that.dataSource; } else { this.dataSource = dataSource; } } public TransactionManager getTransactionManager() { return transactionManager; } public void setTransactionManager(TransactionManager transactionManager) { this.transactionManager = transactionManager; } } ================================================ FILE: bytejta-supports/src/main/java/org/bytesoft/bytejta/supports/jpa/hibernate/HibernateJtaPlatform.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.bytejta.supports.jpa.hibernate; import org.bytesoft.bytejta.TransactionBeanFactoryImpl; import org.bytesoft.transaction.TransactionBeanFactory; import org.hibernate.engine.transaction.jta.platform.internal.AbstractJtaPlatform; public class HibernateJtaPlatform extends AbstractJtaPlatform { private static final long serialVersionUID = 1L; protected javax.transaction.TransactionManager locateTransactionManager() { TransactionBeanFactory beanFactory = TransactionBeanFactoryImpl.getInstance(); return beanFactory == null ? null : beanFactory.getTransactionManager(); } protected javax.transaction.UserTransaction locateUserTransaction() { return null; } } ================================================ FILE: bytejta-supports/src/main/java/org/bytesoft/bytejta/supports/resource/ManagedConnectionFactoryHandler.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 java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import javax.jms.XAConnectionFactory; import javax.resource.spi.ManagedConnection; import javax.resource.spi.ManagedConnectionFactory; import javax.sql.XADataSource; public class ManagedConnectionFactoryHandler implements InvocationHandler { private final Object delegate; private String identifier; public ManagedConnectionFactoryHandler(Object xads) { this.delegate = xads; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Class declaringClass = method.getDeclaringClass(); Class returningClass = method.getReturnType(); Object resultObject = null; try { resultObject = method.invoke(this.delegate, args); } catch (InvocationTargetException ex) { throw ex.getTargetException(); } catch (IllegalAccessException ex) { throw new RuntimeException(ex); } Class clazz = resultObject.getClass(); ClassLoader cl = clazz.getClassLoader(); boolean containsReturningClass = false; Class[] interfaces = clazz.getInterfaces(); for (int i = 0; i < interfaces.length; i++) { Class interfaceClass = interfaces[i]; if (interfaceClass.equals(returningClass)) { containsReturningClass = true; break; } } // end-for (int i = 0; i < interfaces.length; i++) if (XADataSource.class.equals(declaringClass) && javax.sql.XAConnection.class.equals(returningClass)) { ManagedConnectionHandler interceptor = new ManagedConnectionHandler(resultObject); interceptor.setIdentifier(this.identifier); Object finalObject = null; if (containsReturningClass) { finalObject = Proxy.newProxyInstance(cl, interfaces, interceptor); } else { Class[] interfaceArray = new Class[interfaces.length + 1]; System.arraycopy(interfaces, 0, interfaceArray, 0, interfaces.length); interfaceArray[interfaces.length] = javax.sql.XAConnection.class; finalObject = Proxy.newProxyInstance(cl, interfaceArray, interceptor); } return (javax.sql.XAConnection) finalObject; } else if (XAConnectionFactory.class.equals(declaringClass) && javax.jms.XAConnection.class.equals(returningClass)) { ManagedConnectionHandler interceptor = new ManagedConnectionHandler(resultObject); interceptor.setIdentifier(this.identifier); Object finalObject = null; if (containsReturningClass) { finalObject = Proxy.newProxyInstance(cl, interfaces, interceptor); } else { Class[] interfaceArray = new Class[interfaces.length + 1]; System.arraycopy(interfaces, 0, interfaceArray, 0, interfaces.length); interfaceArray[interfaces.length] = javax.jms.XAConnection.class; finalObject = Proxy.newProxyInstance(cl, interfaceArray, interceptor); } return (javax.jms.XAConnection) finalObject; } else if (ManagedConnectionFactory.class.equals(declaringClass) && ManagedConnection.class.equals(returningClass)) { ManagedConnectionHandler interceptor = new ManagedConnectionHandler(resultObject); interceptor.setIdentifier(this.identifier); Object finalObject = null; if (containsReturningClass) { finalObject = Proxy.newProxyInstance(cl, interfaces, interceptor); } else { Class[] interfaceArray = new Class[interfaces.length + 1]; System.arraycopy(interfaces, 0, interfaceArray, 0, interfaces.length); interfaceArray[interfaces.length] = ManagedConnection.class; finalObject = Proxy.newProxyInstance(cl, interfaceArray, interceptor); } return (ManagedConnection) finalObject; } else { return resultObject; } } public Object getDelegate() { return delegate; } public String getIdentifier() { return identifier; } public void setIdentifier(String identifier) { this.identifier = identifier; } } ================================================ FILE: bytejta-supports/src/main/java/org/bytesoft/bytejta/supports/resource/ManagedConnectionHandler.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 java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import javax.jms.XASession; import javax.resource.spi.ManagedConnection; import javax.transaction.xa.XAResource; import org.bytesoft.transaction.supports.resource.XAResourceDescriptor; public class ManagedConnectionHandler implements InvocationHandler { private final Object delegate; private String identifier; public ManagedConnectionHandler(Object managed) { this.delegate = managed; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Class declaringClass = method.getDeclaringClass(); Class returningClass = method.getReturnType(); Object resultObject = null; try { resultObject = method.invoke(this.delegate, args); } catch (InvocationTargetException ex) { throw ex.getTargetException(); } catch (IllegalAccessException ex) { throw new RuntimeException(ex); } if (resultObject != null && XAResourceDescriptor.class.isInstance(resultObject)) { return resultObject; } CommonResourceDescriptor descriptor = new CommonResourceDescriptor(); descriptor.setIdentifier(this.identifier); if (javax.sql.XAConnection.class.equals(declaringClass) && XAResource.class.equals(returningClass)) { descriptor.setDelegate((XAResource) resultObject); } else if (javax.jms.XAConnection.class.equals(declaringClass) && XAResource.class.equals(returningClass)) { descriptor.setDelegate((XAResource) resultObject); } else if (javax.jms.XAConnection.class.equals(declaringClass) && XASession.class.equals(returningClass)) { Class clazz = resultObject.getClass(); ClassLoader cl = clazz.getClassLoader(); Class[] interfaces = clazz.getInterfaces(); boolean containsReturningClass = false; for (int i = 0; i < interfaces.length; i++) { Class interfaceClass = interfaces[i]; if (interfaceClass.equals(returningClass)) { containsReturningClass = true; break; } } // end-for (int i = 0; i < interfaces.length; i++) ManagedXASessionHandler interceptor = new ManagedXASessionHandler(resultObject); interceptor.setIdentifier(this.identifier); if (containsReturningClass) { return Proxy.newProxyInstance(cl, interfaces, interceptor); } else { Class[] interfaceArray = new Class[interfaces.length + 1]; System.arraycopy(interfaces, 0, interfaceArray, 0, interfaces.length); interfaceArray[interfaces.length] = javax.jms.XASession.class; return Proxy.newProxyInstance(cl, interfaceArray, interceptor); } } else if (ManagedConnection.class.equals(declaringClass) && XAResource.class.equals(returningClass)) { descriptor.setDelegate((XAResource) resultObject); } return descriptor.getDelegate() != null ? descriptor : resultObject; } public String getIdentifier() { return identifier; } public void setIdentifier(String identifier) { this.identifier = identifier; } } ================================================ FILE: bytejta-supports/src/main/java/org/bytesoft/bytejta/supports/resource/ManagedXASessionHandler.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 java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import javax.transaction.xa.XAResource; import org.bytesoft.transaction.supports.resource.XAResourceDescriptor; public class ManagedXASessionHandler implements InvocationHandler { private final Object delegate; private String identifier; public ManagedXASessionHandler(Object managed) { this.delegate = managed; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Class declaringClass = method.getDeclaringClass(); Class returningClass = method.getReturnType(); Object resultObject = null; try { resultObject = method.invoke(this.delegate, args); } catch (InvocationTargetException ex) { throw ex.getTargetException(); } catch (IllegalAccessException ex) { throw new RuntimeException(ex); } if (resultObject != null && XAResourceDescriptor.class.isInstance(resultObject)) { return resultObject; } CommonResourceDescriptor descriptor = new CommonResourceDescriptor(); descriptor.setIdentifier(this.identifier); if (javax.jms.XASession.class.equals(declaringClass) && XAResource.class.equals(returningClass)) { descriptor.setDelegate((XAResource) resultObject); } return descriptor.getDelegate() != null ? descriptor : resultObject; } public String getIdentifier() { return identifier; } public void setIdentifier(String identifier) { this.identifier = identifier; } } ================================================ FILE: bytejta-supports/src/main/java/org/bytesoft/bytejta/supports/resource/jdbc/CallableStatementImpl.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.bytejta.supports.resource.jdbc; import java.io.InputStream; import java.io.Reader; import java.math.BigDecimal; import java.net.URL; import java.sql.Array; import java.sql.Blob; import java.sql.CallableStatement; import java.sql.Clob; import java.sql.Connection; import java.sql.Date; import java.sql.NClob; import java.sql.ParameterMetaData; import java.sql.Ref; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.RowId; import java.sql.SQLException; import java.sql.SQLType; import java.sql.SQLWarning; import java.sql.SQLXML; import java.sql.Time; import java.sql.Timestamp; import java.util.Calendar; import java.util.Map; public class CallableStatementImpl implements CallableStatement { private CallableStatement delegate; private ConnectionImpl connection; public T unwrap(Class iface) throws SQLException { return delegate.unwrap(iface); } public ResultSet executeQuery(String sql) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeQuery(sql); } public ResultSet executeQuery() throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeQuery(); } public void registerOutParameter(int parameterIndex, int sqlType) throws SQLException { delegate.registerOutParameter(parameterIndex, sqlType); } public boolean isWrapperFor(Class iface) throws SQLException { return delegate.isWrapperFor(iface); } public int executeUpdate(String sql) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeUpdate(sql); } public int executeUpdate() throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeUpdate(); } public void setNull(int parameterIndex, int sqlType) throws SQLException { delegate.setNull(parameterIndex, sqlType); } public void close() throws SQLException { delegate.close(); } public void registerOutParameter(int parameterIndex, int sqlType, int scale) throws SQLException { delegate.registerOutParameter(parameterIndex, sqlType, scale); } public int getMaxFieldSize() throws SQLException { return delegate.getMaxFieldSize(); } public void setBoolean(int parameterIndex, boolean x) throws SQLException { delegate.setBoolean(parameterIndex, x); } public void setByte(int parameterIndex, byte x) throws SQLException { delegate.setByte(parameterIndex, x); } public void setMaxFieldSize(int max) throws SQLException { delegate.setMaxFieldSize(max); } public boolean wasNull() throws SQLException { return delegate.wasNull(); } public void setShort(int parameterIndex, short x) throws SQLException { delegate.setShort(parameterIndex, x); } public String getString(int parameterIndex) throws SQLException { return delegate.getString(parameterIndex); } public int getMaxRows() throws SQLException { return delegate.getMaxRows(); } public void setInt(int parameterIndex, int x) throws SQLException { delegate.setInt(parameterIndex, x); } public void setMaxRows(int max) throws SQLException { delegate.setMaxRows(max); } public void setLong(int parameterIndex, long x) throws SQLException { delegate.setLong(parameterIndex, x); } public boolean getBoolean(int parameterIndex) throws SQLException { return delegate.getBoolean(parameterIndex); } public void setFloat(int parameterIndex, float x) throws SQLException { delegate.setFloat(parameterIndex, x); } public void setEscapeProcessing(boolean enable) throws SQLException { delegate.setEscapeProcessing(enable); } public byte getByte(int parameterIndex) throws SQLException { return delegate.getByte(parameterIndex); } public void setDouble(int parameterIndex, double x) throws SQLException { delegate.setDouble(parameterIndex, x); } public short getShort(int parameterIndex) throws SQLException { return delegate.getShort(parameterIndex); } public int getQueryTimeout() throws SQLException { return delegate.getQueryTimeout(); } public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException { delegate.setBigDecimal(parameterIndex, x); } public int getInt(int parameterIndex) throws SQLException { return delegate.getInt(parameterIndex); } public void setQueryTimeout(int seconds) throws SQLException { delegate.setQueryTimeout(seconds); } public void setString(int parameterIndex, String x) throws SQLException { delegate.setString(parameterIndex, x); } public long getLong(int parameterIndex) throws SQLException { return delegate.getLong(parameterIndex); } public void setBytes(int parameterIndex, byte[] x) throws SQLException { delegate.setBytes(parameterIndex, x); } public float getFloat(int parameterIndex) throws SQLException { return delegate.getFloat(parameterIndex); } public void cancel() throws SQLException { delegate.cancel(); } public double getDouble(int parameterIndex) throws SQLException { return delegate.getDouble(parameterIndex); } public void setDate(int parameterIndex, Date x) throws SQLException { delegate.setDate(parameterIndex, x); } public SQLWarning getWarnings() throws SQLException { return delegate.getWarnings(); } @SuppressWarnings("deprecation") public BigDecimal getBigDecimal(int parameterIndex, int scale) throws SQLException { return delegate.getBigDecimal(parameterIndex, scale); } public void setTime(int parameterIndex, Time x) throws SQLException { delegate.setTime(parameterIndex, x); } public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException { delegate.setTimestamp(parameterIndex, x); } public void clearWarnings() throws SQLException { delegate.clearWarnings(); } public byte[] getBytes(int parameterIndex) throws SQLException { return delegate.getBytes(parameterIndex); } public void setCursorName(String name) throws SQLException { delegate.setCursorName(name); } public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException { delegate.setAsciiStream(parameterIndex, x, length); } public Date getDate(int parameterIndex) throws SQLException { return delegate.getDate(parameterIndex); } public Time getTime(int parameterIndex) throws SQLException { return delegate.getTime(parameterIndex); } @SuppressWarnings("deprecation") public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException { delegate.setUnicodeStream(parameterIndex, x, length); } public boolean execute(String sql) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.execute(sql); } public Timestamp getTimestamp(int parameterIndex) throws SQLException { return delegate.getTimestamp(parameterIndex); } public Object getObject(int parameterIndex) throws SQLException { return delegate.getObject(parameterIndex); } public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException { delegate.setBinaryStream(parameterIndex, x, length); } public ResultSet getResultSet() throws SQLException { return delegate.getResultSet(); } public BigDecimal getBigDecimal(int parameterIndex) throws SQLException { return delegate.getBigDecimal(parameterIndex); } public int getUpdateCount() throws SQLException { return delegate.getUpdateCount(); } public void clearParameters() throws SQLException { delegate.clearParameters(); } public Object getObject(int parameterIndex, Map> map) throws SQLException { return delegate.getObject(parameterIndex, map); } public boolean getMoreResults() throws SQLException { return delegate.getMoreResults(); } public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException { delegate.setObject(parameterIndex, x, targetSqlType); } public void setFetchDirection(int direction) throws SQLException { delegate.setFetchDirection(direction); } public Ref getRef(int parameterIndex) throws SQLException { return delegate.getRef(parameterIndex); } public void setObject(int parameterIndex, Object x) throws SQLException { delegate.setObject(parameterIndex, x); } public int getFetchDirection() throws SQLException { return delegate.getFetchDirection(); } public Blob getBlob(int parameterIndex) throws SQLException { return delegate.getBlob(parameterIndex); } public void setFetchSize(int rows) throws SQLException { delegate.setFetchSize(rows); } public Clob getClob(int parameterIndex) throws SQLException { return delegate.getClob(parameterIndex); } public int getFetchSize() throws SQLException { return delegate.getFetchSize(); } public boolean execute() throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.execute(); } public Array getArray(int parameterIndex) throws SQLException { return delegate.getArray(parameterIndex); } public int getResultSetConcurrency() throws SQLException { return delegate.getResultSetConcurrency(); } public Date getDate(int parameterIndex, Calendar cal) throws SQLException { return delegate.getDate(parameterIndex, cal); } public int getResultSetType() throws SQLException { return delegate.getResultSetType(); } public void addBatch(String sql) throws SQLException { delegate.addBatch(sql); } public void addBatch() throws SQLException { delegate.addBatch(); } public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException { delegate.setCharacterStream(parameterIndex, reader, length); } public Time getTime(int parameterIndex, Calendar cal) throws SQLException { return delegate.getTime(parameterIndex, cal); } public void clearBatch() throws SQLException { delegate.clearBatch(); } public int[] executeBatch() throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeBatch(); } public Timestamp getTimestamp(int parameterIndex, Calendar cal) throws SQLException { return delegate.getTimestamp(parameterIndex, cal); } public void setRef(int parameterIndex, Ref x) throws SQLException { delegate.setRef(parameterIndex, x); } public void setBlob(int parameterIndex, Blob x) throws SQLException { delegate.setBlob(parameterIndex, x); } public void registerOutParameter(int parameterIndex, int sqlType, String typeName) throws SQLException { delegate.registerOutParameter(parameterIndex, sqlType, typeName); } public void setClob(int parameterIndex, Clob x) throws SQLException { delegate.setClob(parameterIndex, x); } public void setArray(int parameterIndex, Array x) throws SQLException { delegate.setArray(parameterIndex, x); } public Connection getConnection() throws SQLException { return connection; } public ResultSetMetaData getMetaData() throws SQLException { return delegate.getMetaData(); } public void registerOutParameter(String parameterName, int sqlType) throws SQLException { delegate.registerOutParameter(parameterName, sqlType); } public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException { delegate.setDate(parameterIndex, x, cal); } public boolean getMoreResults(int current) throws SQLException { return delegate.getMoreResults(current); } public void registerOutParameter(String parameterName, int sqlType, int scale) throws SQLException { delegate.registerOutParameter(parameterName, sqlType, scale); } public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException { delegate.setTime(parameterIndex, x, cal); } public ResultSet getGeneratedKeys() throws SQLException { return delegate.getGeneratedKeys(); } public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException { delegate.setTimestamp(parameterIndex, x, cal); } public void registerOutParameter(String parameterName, int sqlType, String typeName) throws SQLException { delegate.registerOutParameter(parameterName, sqlType, typeName); } public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeUpdate(sql, autoGeneratedKeys); } public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException { delegate.setNull(parameterIndex, sqlType, typeName); } public URL getURL(int parameterIndex) throws SQLException { return delegate.getURL(parameterIndex); } public int executeUpdate(String sql, int[] columnIndexes) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeUpdate(sql, columnIndexes); } public void setURL(int parameterIndex, URL x) throws SQLException { delegate.setURL(parameterIndex, x); } public void setURL(String parameterName, URL val) throws SQLException { delegate.setURL(parameterName, val); } public ParameterMetaData getParameterMetaData() throws SQLException { return delegate.getParameterMetaData(); } public void setNull(String parameterName, int sqlType) throws SQLException { delegate.setNull(parameterName, sqlType); } public void setRowId(int parameterIndex, RowId x) throws SQLException { delegate.setRowId(parameterIndex, x); } public int executeUpdate(String sql, String[] columnNames) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeUpdate(sql, columnNames); } public void setBoolean(String parameterName, boolean x) throws SQLException { delegate.setBoolean(parameterName, x); } public void setNString(int parameterIndex, String value) throws SQLException { delegate.setNString(parameterIndex, value); } public void setByte(String parameterName, byte x) throws SQLException { delegate.setByte(parameterName, x); } public void setShort(String parameterName, short x) throws SQLException { delegate.setShort(parameterName, x); } public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException { delegate.setNCharacterStream(parameterIndex, value, length); } public boolean execute(String sql, int autoGeneratedKeys) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.execute(sql, autoGeneratedKeys); } public void setInt(String parameterName, int x) throws SQLException { delegate.setInt(parameterName, x); } public void setNClob(int parameterIndex, NClob value) throws SQLException { delegate.setNClob(parameterIndex, value); } public void setLong(String parameterName, long x) throws SQLException { delegate.setLong(parameterName, x); } public void setFloat(String parameterName, float x) throws SQLException { delegate.setFloat(parameterName, x); } public void setClob(int parameterIndex, Reader reader, long length) throws SQLException { delegate.setClob(parameterIndex, reader, length); } public void setDouble(String parameterName, double x) throws SQLException { delegate.setDouble(parameterName, x); } public boolean execute(String sql, int[] columnIndexes) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.execute(sql, columnIndexes); } public void setBigDecimal(String parameterName, BigDecimal x) throws SQLException { delegate.setBigDecimal(parameterName, x); } public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException { delegate.setBlob(parameterIndex, inputStream, length); } public void setString(String parameterName, String x) throws SQLException { delegate.setString(parameterName, x); } public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException { delegate.setNClob(parameterIndex, reader, length); } public void setBytes(String parameterName, byte[] x) throws SQLException { delegate.setBytes(parameterName, x); } public void setDate(String parameterName, Date x) throws SQLException { delegate.setDate(parameterName, x); } public boolean execute(String sql, String[] columnNames) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.execute(sql, columnNames); } public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException { delegate.setSQLXML(parameterIndex, xmlObject); } public void setTime(String parameterName, Time x) throws SQLException { delegate.setTime(parameterName, x); } public void setTimestamp(String parameterName, Timestamp x) throws SQLException { delegate.setTimestamp(parameterName, x); } public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) throws SQLException { delegate.setObject(parameterIndex, x, targetSqlType, scaleOrLength); } public void setAsciiStream(String parameterName, InputStream x, int length) throws SQLException { delegate.setAsciiStream(parameterName, x, length); } public int getResultSetHoldability() throws SQLException { return delegate.getResultSetHoldability(); } public boolean isClosed() throws SQLException { return delegate.isClosed(); } public void setBinaryStream(String parameterName, InputStream x, int length) throws SQLException { delegate.setBinaryStream(parameterName, x, length); } public void setPoolable(boolean poolable) throws SQLException { delegate.setPoolable(poolable); } public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException { delegate.setAsciiStream(parameterIndex, x, length); } public void setObject(String parameterName, Object x, int targetSqlType, int scale) throws SQLException { delegate.setObject(parameterName, x, targetSqlType, scale); } public boolean isPoolable() throws SQLException { return delegate.isPoolable(); } public void closeOnCompletion() throws SQLException { delegate.closeOnCompletion(); } public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException { delegate.setBinaryStream(parameterIndex, x, length); } public boolean isCloseOnCompletion() throws SQLException { return delegate.isCloseOnCompletion(); } public void setObject(String parameterName, Object x, int targetSqlType) throws SQLException { delegate.setObject(parameterName, x, targetSqlType); } public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException { delegate.setCharacterStream(parameterIndex, reader, length); } public long getLargeUpdateCount() throws SQLException { return delegate.getLargeUpdateCount(); } public void setObject(String parameterName, Object x) throws SQLException { delegate.setObject(parameterName, x); } public void setLargeMaxRows(long max) throws SQLException { delegate.setLargeMaxRows(max); } public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException { delegate.setAsciiStream(parameterIndex, x); } public long getLargeMaxRows() throws SQLException { return delegate.getLargeMaxRows(); } public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException { delegate.setBinaryStream(parameterIndex, x); } public long[] executeLargeBatch() throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeLargeBatch(); } public void setCharacterStream(String parameterName, Reader reader, int length) throws SQLException { delegate.setCharacterStream(parameterName, reader, length); } public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException { delegate.setCharacterStream(parameterIndex, reader); } public void setDate(String parameterName, Date x, Calendar cal) throws SQLException { delegate.setDate(parameterName, x, cal); } public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException { delegate.setNCharacterStream(parameterIndex, value); } public void setTime(String parameterName, Time x, Calendar cal) throws SQLException { delegate.setTime(parameterName, x, cal); } public long executeLargeUpdate(String sql) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeLargeUpdate(sql); } public void setClob(int parameterIndex, Reader reader) throws SQLException { delegate.setClob(parameterIndex, reader); } public void setTimestamp(String parameterName, Timestamp x, Calendar cal) throws SQLException { delegate.setTimestamp(parameterName, x, cal); } public long executeLargeUpdate(String sql, int autoGeneratedKeys) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeLargeUpdate(sql, autoGeneratedKeys); } public void setNull(String parameterName, int sqlType, String typeName) throws SQLException { delegate.setNull(parameterName, sqlType, typeName); } public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException { delegate.setBlob(parameterIndex, inputStream); } public void setNClob(int parameterIndex, Reader reader) throws SQLException { delegate.setNClob(parameterIndex, reader); } public String getString(String parameterName) throws SQLException { return delegate.getString(parameterName); } public long executeLargeUpdate(String sql, int[] columnIndexes) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeLargeUpdate(sql, columnIndexes); } public boolean getBoolean(String parameterName) throws SQLException { return delegate.getBoolean(parameterName); } public void setObject(int parameterIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { delegate.setObject(parameterIndex, x, targetSqlType, scaleOrLength); } public byte getByte(String parameterName) throws SQLException { return delegate.getByte(parameterName); } public short getShort(String parameterName) throws SQLException { return delegate.getShort(parameterName); } public long executeLargeUpdate(String sql, String[] columnNames) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeLargeUpdate(sql, columnNames); } public int getInt(String parameterName) throws SQLException { return delegate.getInt(parameterName); } public long getLong(String parameterName) throws SQLException { return delegate.getLong(parameterName); } public void setObject(int parameterIndex, Object x, SQLType targetSqlType) throws SQLException { delegate.setObject(parameterIndex, x, targetSqlType); } public float getFloat(String parameterName) throws SQLException { return delegate.getFloat(parameterName); } public long executeLargeUpdate() throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeLargeUpdate(); } public double getDouble(String parameterName) throws SQLException { return delegate.getDouble(parameterName); } public byte[] getBytes(String parameterName) throws SQLException { return delegate.getBytes(parameterName); } public Date getDate(String parameterName) throws SQLException { return delegate.getDate(parameterName); } public Time getTime(String parameterName) throws SQLException { return delegate.getTime(parameterName); } public Timestamp getTimestamp(String parameterName) throws SQLException { return delegate.getTimestamp(parameterName); } public Object getObject(String parameterName) throws SQLException { return delegate.getObject(parameterName); } public BigDecimal getBigDecimal(String parameterName) throws SQLException { return delegate.getBigDecimal(parameterName); } public Object getObject(String parameterName, Map> map) throws SQLException { return delegate.getObject(parameterName, map); } public Ref getRef(String parameterName) throws SQLException { return delegate.getRef(parameterName); } public Blob getBlob(String parameterName) throws SQLException { return delegate.getBlob(parameterName); } public Clob getClob(String parameterName) throws SQLException { return delegate.getClob(parameterName); } public Array getArray(String parameterName) throws SQLException { return delegate.getArray(parameterName); } public Date getDate(String parameterName, Calendar cal) throws SQLException { return delegate.getDate(parameterName, cal); } public Time getTime(String parameterName, Calendar cal) throws SQLException { return delegate.getTime(parameterName, cal); } public Timestamp getTimestamp(String parameterName, Calendar cal) throws SQLException { return delegate.getTimestamp(parameterName, cal); } public URL getURL(String parameterName) throws SQLException { return delegate.getURL(parameterName); } public RowId getRowId(int parameterIndex) throws SQLException { return delegate.getRowId(parameterIndex); } public RowId getRowId(String parameterName) throws SQLException { return delegate.getRowId(parameterName); } public void setRowId(String parameterName, RowId x) throws SQLException { delegate.setRowId(parameterName, x); } public void setNString(String parameterName, String value) throws SQLException { delegate.setNString(parameterName, value); } public void setNCharacterStream(String parameterName, Reader value, long length) throws SQLException { delegate.setNCharacterStream(parameterName, value, length); } public void setNClob(String parameterName, NClob value) throws SQLException { delegate.setNClob(parameterName, value); } public void setClob(String parameterName, Reader reader, long length) throws SQLException { delegate.setClob(parameterName, reader, length); } public void setBlob(String parameterName, InputStream inputStream, long length) throws SQLException { delegate.setBlob(parameterName, inputStream, length); } public void setNClob(String parameterName, Reader reader, long length) throws SQLException { delegate.setNClob(parameterName, reader, length); } public NClob getNClob(int parameterIndex) throws SQLException { return delegate.getNClob(parameterIndex); } public NClob getNClob(String parameterName) throws SQLException { return delegate.getNClob(parameterName); } public void setSQLXML(String parameterName, SQLXML xmlObject) throws SQLException { delegate.setSQLXML(parameterName, xmlObject); } public SQLXML getSQLXML(int parameterIndex) throws SQLException { return delegate.getSQLXML(parameterIndex); } public SQLXML getSQLXML(String parameterName) throws SQLException { return delegate.getSQLXML(parameterName); } public String getNString(int parameterIndex) throws SQLException { return delegate.getNString(parameterIndex); } public String getNString(String parameterName) throws SQLException { return delegate.getNString(parameterName); } public Reader getNCharacterStream(int parameterIndex) throws SQLException { return delegate.getNCharacterStream(parameterIndex); } public Reader getNCharacterStream(String parameterName) throws SQLException { return delegate.getNCharacterStream(parameterName); } public Reader getCharacterStream(int parameterIndex) throws SQLException { return delegate.getCharacterStream(parameterIndex); } public Reader getCharacterStream(String parameterName) throws SQLException { return delegate.getCharacterStream(parameterName); } public void setBlob(String parameterName, Blob x) throws SQLException { delegate.setBlob(parameterName, x); } public void setClob(String parameterName, Clob x) throws SQLException { delegate.setClob(parameterName, x); } public void setAsciiStream(String parameterName, InputStream x, long length) throws SQLException { delegate.setAsciiStream(parameterName, x, length); } public void setBinaryStream(String parameterName, InputStream x, long length) throws SQLException { delegate.setBinaryStream(parameterName, x, length); } public void setCharacterStream(String parameterName, Reader reader, long length) throws SQLException { delegate.setCharacterStream(parameterName, reader, length); } public void setAsciiStream(String parameterName, InputStream x) throws SQLException { delegate.setAsciiStream(parameterName, x); } public void setBinaryStream(String parameterName, InputStream x) throws SQLException { delegate.setBinaryStream(parameterName, x); } public void setCharacterStream(String parameterName, Reader reader) throws SQLException { delegate.setCharacterStream(parameterName, reader); } public void setNCharacterStream(String parameterName, Reader value) throws SQLException { delegate.setNCharacterStream(parameterName, value); } public void setClob(String parameterName, Reader reader) throws SQLException { delegate.setClob(parameterName, reader); } public void setBlob(String parameterName, InputStream inputStream) throws SQLException { delegate.setBlob(parameterName, inputStream); } public void setNClob(String parameterName, Reader reader) throws SQLException { delegate.setNClob(parameterName, reader); } public T getObject(int parameterIndex, Class type) throws SQLException { return delegate.getObject(parameterIndex, type); } public T getObject(String parameterName, Class type) throws SQLException { return delegate.getObject(parameterName, type); } public void setObject(String parameterName, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { delegate.setObject(parameterName, x, targetSqlType, scaleOrLength); } public void setObject(String parameterName, Object x, SQLType targetSqlType) throws SQLException { delegate.setObject(parameterName, x, targetSqlType); } public void registerOutParameter(int parameterIndex, SQLType sqlType) throws SQLException { delegate.registerOutParameter(parameterIndex, sqlType); } public void registerOutParameter(int parameterIndex, SQLType sqlType, int scale) throws SQLException { delegate.registerOutParameter(parameterIndex, sqlType, scale); } public void registerOutParameter(int parameterIndex, SQLType sqlType, String typeName) throws SQLException { delegate.registerOutParameter(parameterIndex, sqlType, typeName); } public void registerOutParameter(String parameterName, SQLType sqlType) throws SQLException { delegate.registerOutParameter(parameterName, sqlType); } public void registerOutParameter(String parameterName, SQLType sqlType, int scale) throws SQLException { delegate.registerOutParameter(parameterName, sqlType, scale); } public void registerOutParameter(String parameterName, SQLType sqlType, String typeName) throws SQLException { delegate.registerOutParameter(parameterName, sqlType, typeName); } public void setConnection(ConnectionImpl connection) { this.connection = connection; } public CallableStatement getDelegate() { return delegate; } public void setDelegate(CallableStatement delegate) { this.delegate = delegate; } } ================================================ FILE: bytejta-supports/src/main/java/org/bytesoft/bytejta/supports/resource/jdbc/ConnectionImpl.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.bytejta.supports.resource.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 javax.transaction.Status; import org.bytesoft.bytejta.TransactionBeanFactoryImpl; import org.bytesoft.transaction.Transaction; import org.bytesoft.transaction.TransactionBeanFactory; import org.bytesoft.transaction.TransactionManager; public class ConnectionImpl implements Connection { private Connection delegate; private XAConnectionImpl managedConnection; private boolean closed; public T unwrap(Class iface) throws SQLException { return delegate.unwrap(iface); } public boolean isWrapperFor(Class iface) throws SQLException { return delegate.isWrapperFor(iface); } public Statement createStatement() throws SQLException { this.checkTransactionStatusIfNecessary(); StatementImpl statement = new StatementImpl(); statement.setConnection(this); statement.setDelegate(delegate.createStatement()); return statement; } public PreparedStatement prepareStatement(String sql) throws SQLException { this.checkTransactionStatusIfNecessary(); PreparedStatementImpl statement = new PreparedStatementImpl(); statement.setConnection(this); statement.setDelegate(delegate.prepareStatement(sql)); return statement; } public CallableStatement prepareCall(String sql) throws SQLException { this.checkTransactionStatusIfNecessary(); CallableStatementImpl statement = new CallableStatementImpl(); statement.setConnection(this); statement.setDelegate(delegate.prepareCall(sql)); return statement; } public String nativeSQL(String sql) throws SQLException { return delegate.nativeSQL(sql); } public void setAutoCommit(boolean autoCommit) throws SQLException { delegate.setAutoCommit(autoCommit); } public boolean getAutoCommit() throws SQLException { return delegate.getAutoCommit(); } public void commit() throws SQLException { delegate.commit(); } public void rollback() throws SQLException { delegate.rollback(); } public void close() throws SQLException { this.closed = true; delegate.close(); } public boolean isClosed() throws SQLException { return this.closed ? true : delegate.isClosed(); } public DatabaseMetaData getMetaData() throws SQLException { DatabaseMetaDataImpl metadata = new DatabaseMetaDataImpl(); DatabaseMetaData delegateMetadata = delegate.getMetaData(); metadata.setDelegate(delegateMetadata); return metadata; } public void setReadOnly(boolean readOnly) throws SQLException { delegate.setReadOnly(readOnly); } public boolean isReadOnly() throws SQLException { return delegate.isReadOnly(); } public void setCatalog(String catalog) throws SQLException { delegate.setCatalog(catalog); } public String getCatalog() throws SQLException { return delegate.getCatalog(); } public void setTransactionIsolation(int level) throws SQLException { delegate.setTransactionIsolation(level); } public int getTransactionIsolation() throws SQLException { return delegate.getTransactionIsolation(); } public SQLWarning getWarnings() throws SQLException { return delegate.getWarnings(); } public void clearWarnings() throws SQLException { delegate.clearWarnings(); } public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { this.checkTransactionStatusIfNecessary(); StatementImpl statement = new StatementImpl(); statement.setConnection(this); statement.setDelegate(delegate.createStatement(resultSetType, resultSetConcurrency)); return statement; } public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { this.checkTransactionStatusIfNecessary(); PreparedStatementImpl statement = new PreparedStatementImpl(); statement.setConnection(this); statement.setDelegate(delegate.prepareStatement(sql, resultSetType, resultSetConcurrency)); return statement; } public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { this.checkTransactionStatusIfNecessary(); CallableStatementImpl statement = new CallableStatementImpl(); statement.setConnection(this); statement.setDelegate(delegate.prepareCall(sql, resultSetType, resultSetConcurrency)); return statement; } public Map> getTypeMap() throws SQLException { return delegate.getTypeMap(); } public void setTypeMap(Map> map) throws SQLException { delegate.setTypeMap(map); } public void setHoldability(int holdability) throws SQLException { delegate.setHoldability(holdability); } public int getHoldability() throws SQLException { return delegate.getHoldability(); } public Savepoint setSavepoint() throws SQLException { return delegate.setSavepoint(); } public Savepoint setSavepoint(String name) throws SQLException { return delegate.setSavepoint(name); } public void rollback(Savepoint savepoint) throws SQLException { delegate.rollback(savepoint); } public void releaseSavepoint(Savepoint savepoint) throws SQLException { delegate.releaseSavepoint(savepoint); } public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { this.checkTransactionStatusIfNecessary(); StatementImpl statement = new StatementImpl(); statement.setConnection(this); statement.setDelegate(delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability)); return statement; } public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { this.checkTransactionStatusIfNecessary(); PreparedStatementImpl statement = new PreparedStatementImpl(); statement.setConnection(this); statement.setDelegate(delegate.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability)); return statement; } public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { this.checkTransactionStatusIfNecessary(); CallableStatementImpl statement = new CallableStatementImpl(); statement.setConnection(this); statement.setDelegate(delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability)); return statement; } public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { this.checkTransactionStatusIfNecessary(); PreparedStatementImpl statement = new PreparedStatementImpl(); statement.setConnection(this); statement.setDelegate(delegate.prepareStatement(sql, autoGeneratedKeys)); return statement; } public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { this.checkTransactionStatusIfNecessary(); PreparedStatementImpl statement = new PreparedStatementImpl(); statement.setConnection(this); statement.setDelegate(delegate.prepareStatement(sql, columnIndexes)); return statement; } public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { this.checkTransactionStatusIfNecessary(); PreparedStatementImpl statement = new PreparedStatementImpl(); statement.setConnection(this); statement.setDelegate(delegate.prepareStatement(sql, columnNames)); return statement; } public Clob createClob() throws SQLException { return delegate.createClob(); } public Blob createBlob() throws SQLException { return delegate.createBlob(); } public NClob createNClob() throws SQLException { return delegate.createNClob(); } public SQLXML createSQLXML() throws SQLException { return delegate.createSQLXML(); } public boolean isValid(int timeout) throws SQLException { return this.closed ? false : delegate.isValid(timeout); } public void setClientInfo(String name, String value) throws SQLClientInfoException { delegate.setClientInfo(name, value); } public void setClientInfo(Properties properties) throws SQLClientInfoException { delegate.setClientInfo(properties); } public String getClientInfo(String name) throws SQLException { return delegate.getClientInfo(name); } public Properties getClientInfo() throws SQLException { return delegate.getClientInfo(); } public Array createArrayOf(String typeName, Object[] elements) throws SQLException { return delegate.createArrayOf(typeName, elements); } public Struct createStruct(String typeName, Object[] attributes) throws SQLException { return delegate.createStruct(typeName, attributes); } public void setSchema(String schema) throws SQLException { delegate.setSchema(schema); } public String getSchema() throws SQLException { return delegate.getSchema(); } public void abort(Executor executor) throws SQLException { delegate.abort(executor); } public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException { delegate.setNetworkTimeout(executor, milliseconds); } public int getNetworkTimeout() throws SQLException { return delegate.getNetworkTimeout(); } public void checkTransactionStatusIfNecessary() throws SQLException { TransactionBeanFactory beanFactory = TransactionBeanFactoryImpl.getInstance(); TransactionManager transactionManager = beanFactory.getTransactionManager(); Transaction transaction = transactionManager.getTransactionQuietly(); if (transaction == null) { return; // return quietly } // end-if (transaction == null) int transactionStatus = transaction.getTransactionStatus(); if (Status.STATUS_ACTIVE != transactionStatus && Status.STATUS_MARKED_ROLLBACK != transactionStatus) { throw new SQLException("Operation is disabled during the inactive phase of the transaction!"); } else if (transaction.isTiming() == false) { throw new SQLException("Operation is disabled during the inactive phase of the transaction!"); } } public XAConnectionImpl getManagedConnection() { return managedConnection; } public void setManagedConnection(XAConnectionImpl managedConnection) { this.managedConnection = managedConnection; } public Connection getDelegate() { return delegate; } public void setDelegate(Connection delegate) { this.delegate = delegate; } } ================================================ FILE: bytejta-supports/src/main/java/org/bytesoft/bytejta/supports/resource/jdbc/DatabaseMetaDataImpl.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.resource.jdbc; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.RowIdLifetime; import java.sql.SQLException; public class DatabaseMetaDataImpl implements DatabaseMetaData { private DatabaseMetaData delegate; private Connection connection; public T unwrap(Class iface) throws SQLException { return delegate.unwrap(iface); } public boolean isWrapperFor(Class iface) throws SQLException { return delegate.isWrapperFor(iface); } public boolean allProceduresAreCallable() throws SQLException { return delegate.allProceduresAreCallable(); } public boolean allTablesAreSelectable() throws SQLException { return delegate.allTablesAreSelectable(); } public String getURL() throws SQLException { return delegate.getURL(); } public String getUserName() throws SQLException { return delegate.getUserName(); } public boolean isReadOnly() throws SQLException { return delegate.isReadOnly(); } public boolean nullsAreSortedHigh() throws SQLException { return delegate.nullsAreSortedHigh(); } public boolean nullsAreSortedLow() throws SQLException { return delegate.nullsAreSortedLow(); } public boolean nullsAreSortedAtStart() throws SQLException { return delegate.nullsAreSortedAtStart(); } public boolean nullsAreSortedAtEnd() throws SQLException { return delegate.nullsAreSortedAtEnd(); } public String getDatabaseProductName() throws SQLException { return delegate.getDatabaseProductName(); } public String getDatabaseProductVersion() throws SQLException { return delegate.getDatabaseProductVersion(); } public String getDriverName() throws SQLException { return delegate.getDriverName(); } public String getDriverVersion() throws SQLException { return delegate.getDriverVersion(); } public int getDriverMajorVersion() { return delegate.getDriverMajorVersion(); } public int getDriverMinorVersion() { return delegate.getDriverMinorVersion(); } public boolean usesLocalFiles() throws SQLException { return delegate.usesLocalFiles(); } public boolean usesLocalFilePerTable() throws SQLException { return delegate.usesLocalFilePerTable(); } public boolean supportsMixedCaseIdentifiers() throws SQLException { return delegate.supportsMixedCaseIdentifiers(); } public boolean storesUpperCaseIdentifiers() throws SQLException { return delegate.storesUpperCaseIdentifiers(); } public boolean storesLowerCaseIdentifiers() throws SQLException { return delegate.storesLowerCaseIdentifiers(); } public boolean storesMixedCaseIdentifiers() throws SQLException { return delegate.storesMixedCaseIdentifiers(); } public boolean supportsMixedCaseQuotedIdentifiers() throws SQLException { return delegate.supportsMixedCaseQuotedIdentifiers(); } public boolean storesUpperCaseQuotedIdentifiers() throws SQLException { return delegate.storesUpperCaseQuotedIdentifiers(); } public boolean storesLowerCaseQuotedIdentifiers() throws SQLException { return delegate.storesLowerCaseQuotedIdentifiers(); } public boolean storesMixedCaseQuotedIdentifiers() throws SQLException { return delegate.storesMixedCaseQuotedIdentifiers(); } public String getIdentifierQuoteString() throws SQLException { return delegate.getIdentifierQuoteString(); } public String getSQLKeywords() throws SQLException { return delegate.getSQLKeywords(); } public String getNumericFunctions() throws SQLException { return delegate.getNumericFunctions(); } public String getStringFunctions() throws SQLException { return delegate.getStringFunctions(); } public String getSystemFunctions() throws SQLException { return delegate.getSystemFunctions(); } public String getTimeDateFunctions() throws SQLException { return delegate.getTimeDateFunctions(); } public String getSearchStringEscape() throws SQLException { return delegate.getSearchStringEscape(); } public String getExtraNameCharacters() throws SQLException { return delegate.getExtraNameCharacters(); } public boolean supportsAlterTableWithAddColumn() throws SQLException { return delegate.supportsAlterTableWithAddColumn(); } public boolean supportsAlterTableWithDropColumn() throws SQLException { return delegate.supportsAlterTableWithDropColumn(); } public boolean supportsColumnAliasing() throws SQLException { return delegate.supportsColumnAliasing(); } public boolean nullPlusNonNullIsNull() throws SQLException { return delegate.nullPlusNonNullIsNull(); } public boolean supportsConvert() throws SQLException { return delegate.supportsConvert(); } public boolean supportsConvert(int fromType, int toType) throws SQLException { return delegate.supportsConvert(fromType, toType); } public boolean supportsTableCorrelationNames() throws SQLException { return delegate.supportsTableCorrelationNames(); } public boolean supportsDifferentTableCorrelationNames() throws SQLException { return delegate.supportsDifferentTableCorrelationNames(); } public boolean supportsExpressionsInOrderBy() throws SQLException { return delegate.supportsExpressionsInOrderBy(); } public boolean supportsOrderByUnrelated() throws SQLException { return delegate.supportsOrderByUnrelated(); } public boolean supportsGroupBy() throws SQLException { return delegate.supportsGroupBy(); } public boolean supportsGroupByUnrelated() throws SQLException { return delegate.supportsGroupByUnrelated(); } public boolean supportsGroupByBeyondSelect() throws SQLException { return delegate.supportsGroupByBeyondSelect(); } public boolean supportsLikeEscapeClause() throws SQLException { return delegate.supportsLikeEscapeClause(); } public boolean supportsMultipleResultSets() throws SQLException { return delegate.supportsMultipleResultSets(); } public boolean supportsMultipleTransactions() throws SQLException { return delegate.supportsMultipleTransactions(); } public boolean supportsNonNullableColumns() throws SQLException { return delegate.supportsNonNullableColumns(); } public boolean supportsMinimumSQLGrammar() throws SQLException { return delegate.supportsMinimumSQLGrammar(); } public boolean supportsCoreSQLGrammar() throws SQLException { return delegate.supportsCoreSQLGrammar(); } public boolean supportsExtendedSQLGrammar() throws SQLException { return delegate.supportsExtendedSQLGrammar(); } public boolean supportsANSI92EntryLevelSQL() throws SQLException { return delegate.supportsANSI92EntryLevelSQL(); } public boolean supportsANSI92IntermediateSQL() throws SQLException { return delegate.supportsANSI92IntermediateSQL(); } public boolean supportsANSI92FullSQL() throws SQLException { return delegate.supportsANSI92FullSQL(); } public boolean supportsIntegrityEnhancementFacility() throws SQLException { return delegate.supportsIntegrityEnhancementFacility(); } public boolean supportsOuterJoins() throws SQLException { return delegate.supportsOuterJoins(); } public boolean supportsFullOuterJoins() throws SQLException { return delegate.supportsFullOuterJoins(); } public boolean supportsLimitedOuterJoins() throws SQLException { return delegate.supportsLimitedOuterJoins(); } public String getSchemaTerm() throws SQLException { return delegate.getSchemaTerm(); } public String getProcedureTerm() throws SQLException { return delegate.getProcedureTerm(); } public String getCatalogTerm() throws SQLException { return delegate.getCatalogTerm(); } public boolean isCatalogAtStart() throws SQLException { return delegate.isCatalogAtStart(); } public String getCatalogSeparator() throws SQLException { return delegate.getCatalogSeparator(); } public boolean supportsSchemasInDataManipulation() throws SQLException { return delegate.supportsSchemasInDataManipulation(); } public boolean supportsSchemasInProcedureCalls() throws SQLException { return delegate.supportsSchemasInProcedureCalls(); } public boolean supportsSchemasInTableDefinitions() throws SQLException { return delegate.supportsSchemasInTableDefinitions(); } public boolean supportsSchemasInIndexDefinitions() throws SQLException { return delegate.supportsSchemasInIndexDefinitions(); } public boolean supportsSchemasInPrivilegeDefinitions() throws SQLException { return delegate.supportsSchemasInPrivilegeDefinitions(); } public boolean supportsCatalogsInDataManipulation() throws SQLException { return delegate.supportsCatalogsInDataManipulation(); } public boolean supportsCatalogsInProcedureCalls() throws SQLException { return delegate.supportsCatalogsInProcedureCalls(); } public boolean supportsCatalogsInTableDefinitions() throws SQLException { return delegate.supportsCatalogsInTableDefinitions(); } public boolean supportsCatalogsInIndexDefinitions() throws SQLException { return delegate.supportsCatalogsInIndexDefinitions(); } public boolean supportsCatalogsInPrivilegeDefinitions() throws SQLException { return delegate.supportsCatalogsInPrivilegeDefinitions(); } public boolean supportsPositionedDelete() throws SQLException { return delegate.supportsPositionedDelete(); } public boolean supportsPositionedUpdate() throws SQLException { return delegate.supportsPositionedUpdate(); } public boolean supportsSelectForUpdate() throws SQLException { return delegate.supportsSelectForUpdate(); } public boolean supportsStoredProcedures() throws SQLException { return delegate.supportsStoredProcedures(); } public boolean supportsSubqueriesInComparisons() throws SQLException { return delegate.supportsSubqueriesInComparisons(); } public boolean supportsSubqueriesInExists() throws SQLException { return delegate.supportsSubqueriesInExists(); } public boolean supportsSubqueriesInIns() throws SQLException { return delegate.supportsSubqueriesInIns(); } public boolean supportsSubqueriesInQuantifieds() throws SQLException { return delegate.supportsSubqueriesInQuantifieds(); } public boolean supportsCorrelatedSubqueries() throws SQLException { return delegate.supportsCorrelatedSubqueries(); } public boolean supportsUnion() throws SQLException { return delegate.supportsUnion(); } public boolean supportsUnionAll() throws SQLException { return delegate.supportsUnionAll(); } public boolean supportsOpenCursorsAcrossCommit() throws SQLException { return delegate.supportsOpenCursorsAcrossCommit(); } public boolean supportsOpenCursorsAcrossRollback() throws SQLException { return delegate.supportsOpenCursorsAcrossRollback(); } public boolean supportsOpenStatementsAcrossCommit() throws SQLException { return delegate.supportsOpenStatementsAcrossCommit(); } public boolean supportsOpenStatementsAcrossRollback() throws SQLException { return delegate.supportsOpenStatementsAcrossRollback(); } public int getMaxBinaryLiteralLength() throws SQLException { return delegate.getMaxBinaryLiteralLength(); } public int getMaxCharLiteralLength() throws SQLException { return delegate.getMaxCharLiteralLength(); } public int getMaxColumnNameLength() throws SQLException { return delegate.getMaxColumnNameLength(); } public int getMaxColumnsInGroupBy() throws SQLException { return delegate.getMaxColumnsInGroupBy(); } public int getMaxColumnsInIndex() throws SQLException { return delegate.getMaxColumnsInIndex(); } public int getMaxColumnsInOrderBy() throws SQLException { return delegate.getMaxColumnsInOrderBy(); } public int getMaxColumnsInSelect() throws SQLException { return delegate.getMaxColumnsInSelect(); } public int getMaxColumnsInTable() throws SQLException { return delegate.getMaxColumnsInTable(); } public int getMaxConnections() throws SQLException { return delegate.getMaxConnections(); } public int getMaxCursorNameLength() throws SQLException { return delegate.getMaxCursorNameLength(); } public int getMaxIndexLength() throws SQLException { return delegate.getMaxIndexLength(); } public int getMaxSchemaNameLength() throws SQLException { return delegate.getMaxSchemaNameLength(); } public int getMaxProcedureNameLength() throws SQLException { return delegate.getMaxProcedureNameLength(); } public int getMaxCatalogNameLength() throws SQLException { return delegate.getMaxCatalogNameLength(); } public int getMaxRowSize() throws SQLException { return delegate.getMaxRowSize(); } public boolean doesMaxRowSizeIncludeBlobs() throws SQLException { return delegate.doesMaxRowSizeIncludeBlobs(); } public int getMaxStatementLength() throws SQLException { return delegate.getMaxStatementLength(); } public int getMaxStatements() throws SQLException { return delegate.getMaxStatements(); } public int getMaxTableNameLength() throws SQLException { return delegate.getMaxTableNameLength(); } public int getMaxTablesInSelect() throws SQLException { return delegate.getMaxTablesInSelect(); } public int getMaxUserNameLength() throws SQLException { return delegate.getMaxUserNameLength(); } public int getDefaultTransactionIsolation() throws SQLException { return delegate.getDefaultTransactionIsolation(); } public boolean supportsTransactions() throws SQLException { return delegate.supportsTransactions(); } public boolean supportsTransactionIsolationLevel(int level) throws SQLException { return delegate.supportsTransactionIsolationLevel(level); } public boolean supportsDataDefinitionAndDataManipulationTransactions() throws SQLException { return delegate.supportsDataDefinitionAndDataManipulationTransactions(); } public boolean supportsDataManipulationTransactionsOnly() throws SQLException { return delegate.supportsDataManipulationTransactionsOnly(); } public boolean dataDefinitionCausesTransactionCommit() throws SQLException { return delegate.dataDefinitionCausesTransactionCommit(); } public boolean dataDefinitionIgnoredInTransactions() throws SQLException { return delegate.dataDefinitionIgnoredInTransactions(); } public ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern) throws SQLException { return delegate.getProcedures(catalog, schemaPattern, procedureNamePattern); } public ResultSet getProcedureColumns(String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) throws SQLException { return delegate.getProcedureColumns(catalog, schemaPattern, procedureNamePattern, columnNamePattern); } public ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, String[] types) throws SQLException { return delegate.getTables(catalog, schemaPattern, tableNamePattern, types); } public ResultSet getSchemas() throws SQLException { return delegate.getSchemas(); } public ResultSet getCatalogs() throws SQLException { return delegate.getCatalogs(); } public ResultSet getTableTypes() throws SQLException { return delegate.getTableTypes(); } public ResultSet getColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) throws SQLException { return delegate.getColumns(catalog, schemaPattern, tableNamePattern, columnNamePattern); } public ResultSet getColumnPrivileges(String catalog, String schema, String table, String columnNamePattern) throws SQLException { return delegate.getColumnPrivileges(catalog, schema, table, columnNamePattern); } public ResultSet getTablePrivileges(String catalog, String schemaPattern, String tableNamePattern) throws SQLException { return delegate.getTablePrivileges(catalog, schemaPattern, tableNamePattern); } public ResultSet getBestRowIdentifier(String catalog, String schema, String table, int scope, boolean nullable) throws SQLException { return delegate.getBestRowIdentifier(catalog, schema, table, scope, nullable); } public ResultSet getVersionColumns(String catalog, String schema, String table) throws SQLException { return delegate.getVersionColumns(catalog, schema, table); } public ResultSet getPrimaryKeys(String catalog, String schema, String table) throws SQLException { return delegate.getPrimaryKeys(catalog, schema, table); } public ResultSet getImportedKeys(String catalog, String schema, String table) throws SQLException { return delegate.getImportedKeys(catalog, schema, table); } public ResultSet getExportedKeys(String catalog, String schema, String table) throws SQLException { return delegate.getExportedKeys(catalog, schema, table); } public ResultSet getCrossReference(String parentCatalog, String parentSchema, String parentTable, String foreignCatalog, String foreignSchema, String foreignTable) throws SQLException { return delegate.getCrossReference(parentCatalog, parentSchema, parentTable, foreignCatalog, foreignSchema, foreignTable); } public ResultSet getTypeInfo() throws SQLException { return delegate.getTypeInfo(); } public ResultSet getIndexInfo(String catalog, String schema, String table, boolean unique, boolean approximate) throws SQLException { return delegate.getIndexInfo(catalog, schema, table, unique, approximate); } public boolean supportsResultSetType(int type) throws SQLException { return delegate.supportsResultSetType(type); } public boolean supportsResultSetConcurrency(int type, int concurrency) throws SQLException { return delegate.supportsResultSetConcurrency(type, concurrency); } public boolean ownUpdatesAreVisible(int type) throws SQLException { return delegate.ownUpdatesAreVisible(type); } public boolean ownDeletesAreVisible(int type) throws SQLException { return delegate.ownDeletesAreVisible(type); } public boolean ownInsertsAreVisible(int type) throws SQLException { return delegate.ownInsertsAreVisible(type); } public boolean othersUpdatesAreVisible(int type) throws SQLException { return delegate.othersUpdatesAreVisible(type); } public boolean othersDeletesAreVisible(int type) throws SQLException { return delegate.othersDeletesAreVisible(type); } public boolean othersInsertsAreVisible(int type) throws SQLException { return delegate.othersInsertsAreVisible(type); } public boolean updatesAreDetected(int type) throws SQLException { return delegate.updatesAreDetected(type); } public boolean deletesAreDetected(int type) throws SQLException { return delegate.deletesAreDetected(type); } public boolean insertsAreDetected(int type) throws SQLException { return delegate.insertsAreDetected(type); } public boolean supportsBatchUpdates() throws SQLException { return delegate.supportsBatchUpdates(); } public ResultSet getUDTs(String catalog, String schemaPattern, String typeNamePattern, int[] types) throws SQLException { return delegate.getUDTs(catalog, schemaPattern, typeNamePattern, types); } public Connection getConnection() throws SQLException { return connection; } public boolean supportsSavepoints() throws SQLException { return delegate.supportsSavepoints(); } public boolean supportsNamedParameters() throws SQLException { return delegate.supportsNamedParameters(); } public boolean supportsMultipleOpenResults() throws SQLException { return delegate.supportsMultipleOpenResults(); } public boolean supportsGetGeneratedKeys() throws SQLException { return delegate.supportsGetGeneratedKeys(); } public ResultSet getSuperTypes(String catalog, String schemaPattern, String typeNamePattern) throws SQLException { return delegate.getSuperTypes(catalog, schemaPattern, typeNamePattern); } public ResultSet getSuperTables(String catalog, String schemaPattern, String tableNamePattern) throws SQLException { return delegate.getSuperTables(catalog, schemaPattern, tableNamePattern); } public ResultSet getAttributes(String catalog, String schemaPattern, String typeNamePattern, String attributeNamePattern) throws SQLException { return delegate.getAttributes(catalog, schemaPattern, typeNamePattern, attributeNamePattern); } public boolean supportsResultSetHoldability(int holdability) throws SQLException { return delegate.supportsResultSetHoldability(holdability); } public int getResultSetHoldability() throws SQLException { return delegate.getResultSetHoldability(); } public int getDatabaseMajorVersion() throws SQLException { return delegate.getDatabaseMajorVersion(); } public int getDatabaseMinorVersion() throws SQLException { return delegate.getDatabaseMinorVersion(); } public int getJDBCMajorVersion() throws SQLException { return delegate.getJDBCMajorVersion(); } public int getJDBCMinorVersion() throws SQLException { return delegate.getJDBCMinorVersion(); } public int getSQLStateType() throws SQLException { return delegate.getSQLStateType(); } public boolean locatorsUpdateCopy() throws SQLException { return delegate.locatorsUpdateCopy(); } public boolean supportsStatementPooling() throws SQLException { return delegate.supportsStatementPooling(); } public RowIdLifetime getRowIdLifetime() throws SQLException { return delegate.getRowIdLifetime(); } public ResultSet getSchemas(String catalog, String schemaPattern) throws SQLException { return delegate.getSchemas(catalog, schemaPattern); } public boolean supportsStoredFunctionsUsingCallSyntax() throws SQLException { return delegate.supportsStoredFunctionsUsingCallSyntax(); } public boolean autoCommitFailureClosesAllResultSets() throws SQLException { return delegate.autoCommitFailureClosesAllResultSets(); } public ResultSet getClientInfoProperties() throws SQLException { return delegate.getClientInfoProperties(); } public ResultSet getFunctions(String catalog, String schemaPattern, String functionNamePattern) throws SQLException { return delegate.getFunctions(catalog, schemaPattern, functionNamePattern); } public ResultSet getFunctionColumns(String catalog, String schemaPattern, String functionNamePattern, String columnNamePattern) throws SQLException { return delegate.getFunctionColumns(catalog, schemaPattern, functionNamePattern, columnNamePattern); } public ResultSet getPseudoColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) throws SQLException { return delegate.getPseudoColumns(catalog, schemaPattern, tableNamePattern, columnNamePattern); } public boolean generatedKeyAlwaysReturned() throws SQLException { return delegate.generatedKeyAlwaysReturned(); } public long getMaxLogicalLobSize() throws SQLException { return delegate.getMaxLogicalLobSize(); } public boolean supportsRefCursors() throws SQLException { return delegate.supportsRefCursors(); } public void setConnection(Connection connection) { this.connection = connection; } public DatabaseMetaData getDelegate() { return delegate; } public void setDelegate(DatabaseMetaData delegate) { this.delegate = delegate; } } ================================================ FILE: bytejta-supports/src/main/java/org/bytesoft/bytejta/supports/resource/jdbc/PreparedStatementImpl.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.bytejta.supports.resource.jdbc; import java.io.InputStream; import java.io.Reader; import java.math.BigDecimal; import java.net.URL; import java.sql.Array; import java.sql.Blob; import java.sql.Clob; import java.sql.Connection; import java.sql.Date; import java.sql.NClob; import java.sql.ParameterMetaData; import java.sql.PreparedStatement; import java.sql.Ref; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.RowId; import java.sql.SQLException; import java.sql.SQLType; import java.sql.SQLWarning; import java.sql.SQLXML; import java.sql.Time; import java.sql.Timestamp; import java.util.Calendar; public class PreparedStatementImpl implements PreparedStatement { private PreparedStatement delegate; private ConnectionImpl connection; public T unwrap(Class iface) throws SQLException { return delegate.unwrap(iface); } public ResultSet executeQuery(String sql) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeQuery(sql); } public ResultSet executeQuery() throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeQuery(); } public boolean isWrapperFor(Class iface) throws SQLException { return delegate.isWrapperFor(iface); } public int executeUpdate(String sql) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeUpdate(sql); } public int executeUpdate() throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeUpdate(); } public void setNull(int parameterIndex, int sqlType) throws SQLException { delegate.setNull(parameterIndex, sqlType); } public void close() throws SQLException { delegate.close(); } public int getMaxFieldSize() throws SQLException { return delegate.getMaxFieldSize(); } public void setBoolean(int parameterIndex, boolean x) throws SQLException { delegate.setBoolean(parameterIndex, x); } public void setByte(int parameterIndex, byte x) throws SQLException { delegate.setByte(parameterIndex, x); } public void setMaxFieldSize(int max) throws SQLException { delegate.setMaxFieldSize(max); } public void setShort(int parameterIndex, short x) throws SQLException { delegate.setShort(parameterIndex, x); } public int getMaxRows() throws SQLException { return delegate.getMaxRows(); } public void setInt(int parameterIndex, int x) throws SQLException { delegate.setInt(parameterIndex, x); } public void setMaxRows(int max) throws SQLException { delegate.setMaxRows(max); } public void setLong(int parameterIndex, long x) throws SQLException { delegate.setLong(parameterIndex, x); } public void setFloat(int parameterIndex, float x) throws SQLException { delegate.setFloat(parameterIndex, x); } public void setEscapeProcessing(boolean enable) throws SQLException { delegate.setEscapeProcessing(enable); } public void setDouble(int parameterIndex, double x) throws SQLException { delegate.setDouble(parameterIndex, x); } public int getQueryTimeout() throws SQLException { return delegate.getQueryTimeout(); } public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException { delegate.setBigDecimal(parameterIndex, x); } public void setQueryTimeout(int seconds) throws SQLException { delegate.setQueryTimeout(seconds); } public void setString(int parameterIndex, String x) throws SQLException { delegate.setString(parameterIndex, x); } public void setBytes(int parameterIndex, byte[] x) throws SQLException { delegate.setBytes(parameterIndex, x); } public void cancel() throws SQLException { delegate.cancel(); } public void setDate(int parameterIndex, Date x) throws SQLException { delegate.setDate(parameterIndex, x); } public SQLWarning getWarnings() throws SQLException { return delegate.getWarnings(); } public void setTime(int parameterIndex, Time x) throws SQLException { delegate.setTime(parameterIndex, x); } public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException { delegate.setTimestamp(parameterIndex, x); } public void clearWarnings() throws SQLException { delegate.clearWarnings(); } public void setCursorName(String name) throws SQLException { delegate.setCursorName(name); } public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException { delegate.setAsciiStream(parameterIndex, x, length); } @SuppressWarnings("deprecation") public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException { delegate.setUnicodeStream(parameterIndex, x, length); } public boolean execute(String sql) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.execute(sql); } public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException { delegate.setBinaryStream(parameterIndex, x, length); } public ResultSet getResultSet() throws SQLException { return delegate.getResultSet(); } public int getUpdateCount() throws SQLException { return delegate.getUpdateCount(); } public void clearParameters() throws SQLException { delegate.clearParameters(); } public boolean getMoreResults() throws SQLException { return delegate.getMoreResults(); } public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException { delegate.setObject(parameterIndex, x, targetSqlType); } public void setFetchDirection(int direction) throws SQLException { delegate.setFetchDirection(direction); } public void setObject(int parameterIndex, Object x) throws SQLException { delegate.setObject(parameterIndex, x); } public int getFetchDirection() throws SQLException { return delegate.getFetchDirection(); } public void setFetchSize(int rows) throws SQLException { delegate.setFetchSize(rows); } public int getFetchSize() throws SQLException { return delegate.getFetchSize(); } public boolean execute() throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.execute(); } public int getResultSetConcurrency() throws SQLException { return delegate.getResultSetConcurrency(); } public int getResultSetType() throws SQLException { return delegate.getResultSetType(); } public void addBatch(String sql) throws SQLException { delegate.addBatch(sql); } public void addBatch() throws SQLException { delegate.addBatch(); } public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException { delegate.setCharacterStream(parameterIndex, reader, length); } public void clearBatch() throws SQLException { delegate.clearBatch(); } public int[] executeBatch() throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeBatch(); } public void setRef(int parameterIndex, Ref x) throws SQLException { delegate.setRef(parameterIndex, x); } public void setBlob(int parameterIndex, Blob x) throws SQLException { delegate.setBlob(parameterIndex, x); } public void setClob(int parameterIndex, Clob x) throws SQLException { delegate.setClob(parameterIndex, x); } public void setArray(int parameterIndex, Array x) throws SQLException { delegate.setArray(parameterIndex, x); } public Connection getConnection() throws SQLException { return connection; } public ResultSetMetaData getMetaData() throws SQLException { return delegate.getMetaData(); } public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException { delegate.setDate(parameterIndex, x, cal); } public boolean getMoreResults(int current) throws SQLException { return delegate.getMoreResults(current); } public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException { delegate.setTime(parameterIndex, x, cal); } public ResultSet getGeneratedKeys() throws SQLException { return delegate.getGeneratedKeys(); } public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException { delegate.setTimestamp(parameterIndex, x, cal); } public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeUpdate(sql, autoGeneratedKeys); } public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException { delegate.setNull(parameterIndex, sqlType, typeName); } public int executeUpdate(String sql, int[] columnIndexes) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeUpdate(sql, columnIndexes); } public void setURL(int parameterIndex, URL x) throws SQLException { delegate.setURL(parameterIndex, x); } public ParameterMetaData getParameterMetaData() throws SQLException { return delegate.getParameterMetaData(); } public void setRowId(int parameterIndex, RowId x) throws SQLException { delegate.setRowId(parameterIndex, x); } public int executeUpdate(String sql, String[] columnNames) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeUpdate(sql, columnNames); } public void setNString(int parameterIndex, String value) throws SQLException { delegate.setNString(parameterIndex, value); } public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException { delegate.setNCharacterStream(parameterIndex, value, length); } public boolean execute(String sql, int autoGeneratedKeys) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.execute(sql, autoGeneratedKeys); } public void setNClob(int parameterIndex, NClob value) throws SQLException { delegate.setNClob(parameterIndex, value); } public void setClob(int parameterIndex, Reader reader, long length) throws SQLException { delegate.setClob(parameterIndex, reader, length); } public boolean execute(String sql, int[] columnIndexes) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.execute(sql, columnIndexes); } public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException { delegate.setBlob(parameterIndex, inputStream, length); } public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException { delegate.setNClob(parameterIndex, reader, length); } public boolean execute(String sql, String[] columnNames) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.execute(sql, columnNames); } public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException { delegate.setSQLXML(parameterIndex, xmlObject); } public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) throws SQLException { delegate.setObject(parameterIndex, x, targetSqlType, scaleOrLength); } public int getResultSetHoldability() throws SQLException { return delegate.getResultSetHoldability(); } public boolean isClosed() throws SQLException { return delegate.isClosed(); } public void setPoolable(boolean poolable) throws SQLException { delegate.setPoolable(poolable); } public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException { delegate.setAsciiStream(parameterIndex, x, length); } public boolean isPoolable() throws SQLException { return delegate.isPoolable(); } public void closeOnCompletion() throws SQLException { delegate.closeOnCompletion(); } public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException { delegate.setBinaryStream(parameterIndex, x, length); } public boolean isCloseOnCompletion() throws SQLException { return delegate.isCloseOnCompletion(); } public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException { delegate.setCharacterStream(parameterIndex, reader, length); } public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException { delegate.setAsciiStream(parameterIndex, x); } public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException { delegate.setBinaryStream(parameterIndex, x); } public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException { delegate.setCharacterStream(parameterIndex, reader); } public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException { delegate.setNCharacterStream(parameterIndex, value); } public void setClob(int parameterIndex, Reader reader) throws SQLException { delegate.setClob(parameterIndex, reader); } public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException { delegate.setBlob(parameterIndex, inputStream); } public void setNClob(int parameterIndex, Reader reader) throws SQLException { delegate.setNClob(parameterIndex, reader); } public long getLargeUpdateCount() throws SQLException { return delegate.getLargeUpdateCount(); } public void setLargeMaxRows(long max) throws SQLException { delegate.setLargeMaxRows(max); } public long getLargeMaxRows() throws SQLException { return delegate.getLargeMaxRows(); } public long[] executeLargeBatch() throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeLargeBatch(); } public long executeLargeUpdate(String sql) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeLargeUpdate(sql); } public long executeLargeUpdate(String sql, int autoGeneratedKeys) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeLargeUpdate(sql, autoGeneratedKeys); } public long executeLargeUpdate(String sql, int[] columnIndexes) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeLargeUpdate(sql, columnIndexes); } public void setObject(int parameterIndex, Object x, SQLType targetSqlType, int scaleOrLength) throws SQLException { delegate.setObject(parameterIndex, x, targetSqlType, scaleOrLength); } public long executeLargeUpdate(String sql, String[] columnNames) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeLargeUpdate(sql, columnNames); } public void setObject(int parameterIndex, Object x, SQLType targetSqlType) throws SQLException { delegate.setObject(parameterIndex, x, targetSqlType); } public long executeLargeUpdate() throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeLargeUpdate(); } public void setConnection(ConnectionImpl connection) { this.connection = connection; } public PreparedStatement getDelegate() { return delegate; } public void setDelegate(PreparedStatement delegate) { this.delegate = delegate; } } ================================================ FILE: bytejta-supports/src/main/java/org/bytesoft/bytejta/supports/resource/jdbc/StatementImpl.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.bytejta.supports.resource.jdbc; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.SQLWarning; import java.sql.Statement; public class StatementImpl implements Statement { private Statement delegate; private ConnectionImpl connection; public T unwrap(Class iface) throws SQLException { return delegate.unwrap(iface); } public ResultSet executeQuery(String sql) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeQuery(sql); } public boolean isWrapperFor(Class iface) throws SQLException { return delegate.isWrapperFor(iface); } public int executeUpdate(String sql) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeUpdate(sql); } public void close() throws SQLException { delegate.close(); } public int getMaxFieldSize() throws SQLException { return delegate.getMaxFieldSize(); } public void setMaxFieldSize(int max) throws SQLException { delegate.setMaxFieldSize(max); } public int getMaxRows() throws SQLException { return delegate.getMaxRows(); } public void setMaxRows(int max) throws SQLException { delegate.setMaxRows(max); } public void setEscapeProcessing(boolean enable) throws SQLException { delegate.setEscapeProcessing(enable); } public int getQueryTimeout() throws SQLException { return delegate.getQueryTimeout(); } public void setQueryTimeout(int seconds) throws SQLException { delegate.setQueryTimeout(seconds); } public void cancel() throws SQLException { delegate.cancel(); } public SQLWarning getWarnings() throws SQLException { return delegate.getWarnings(); } public void clearWarnings() throws SQLException { delegate.clearWarnings(); } public void setCursorName(String name) throws SQLException { delegate.setCursorName(name); } public boolean execute(String sql) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.execute(sql); } public ResultSet getResultSet() throws SQLException { return delegate.getResultSet(); } public int getUpdateCount() throws SQLException { return delegate.getUpdateCount(); } public boolean getMoreResults() throws SQLException { return delegate.getMoreResults(); } public void setFetchDirection(int direction) throws SQLException { delegate.setFetchDirection(direction); } public int getFetchDirection() throws SQLException { return delegate.getFetchDirection(); } public void setFetchSize(int rows) throws SQLException { delegate.setFetchSize(rows); } public int getFetchSize() throws SQLException { return delegate.getFetchSize(); } public int getResultSetConcurrency() throws SQLException { return delegate.getResultSetConcurrency(); } public int getResultSetType() throws SQLException { return delegate.getResultSetType(); } public void addBatch(String sql) throws SQLException { delegate.addBatch(sql); } public void clearBatch() throws SQLException { delegate.clearBatch(); } public int[] executeBatch() throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeBatch(); } public Connection getConnection() throws SQLException { return connection; } public boolean getMoreResults(int current) throws SQLException { return delegate.getMoreResults(current); } public ResultSet getGeneratedKeys() throws SQLException { return delegate.getGeneratedKeys(); } public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeUpdate(sql, autoGeneratedKeys); } public int executeUpdate(String sql, int[] columnIndexes) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeUpdate(sql, columnIndexes); } public int executeUpdate(String sql, String[] columnNames) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeUpdate(sql, columnNames); } public boolean execute(String sql, int autoGeneratedKeys) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.execute(sql, autoGeneratedKeys); } public boolean execute(String sql, int[] columnIndexes) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.execute(sql, columnIndexes); } public boolean execute(String sql, String[] columnNames) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.execute(sql, columnNames); } public int getResultSetHoldability() throws SQLException { return delegate.getResultSetHoldability(); } public boolean isClosed() throws SQLException { return delegate.isClosed(); } public void setPoolable(boolean poolable) throws SQLException { delegate.setPoolable(poolable); } public boolean isPoolable() throws SQLException { return delegate.isPoolable(); } public void closeOnCompletion() throws SQLException { delegate.closeOnCompletion(); } public boolean isCloseOnCompletion() throws SQLException { return delegate.isCloseOnCompletion(); } public long getLargeUpdateCount() throws SQLException { return delegate.getLargeUpdateCount(); } public void setLargeMaxRows(long max) throws SQLException { delegate.setLargeMaxRows(max); } public long getLargeMaxRows() throws SQLException { return delegate.getLargeMaxRows(); } public long[] executeLargeBatch() throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeLargeBatch(); } public long executeLargeUpdate(String sql) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeLargeUpdate(sql); } public long executeLargeUpdate(String sql, int autoGeneratedKeys) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeLargeUpdate(sql, autoGeneratedKeys); } public long executeLargeUpdate(String sql, int[] columnIndexes) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeLargeUpdate(sql, columnIndexes); } public long executeLargeUpdate(String sql, String[] columnNames) throws SQLException { this.connection.checkTransactionStatusIfNecessary(); return delegate.executeLargeUpdate(sql, columnNames); } public void setConnection(ConnectionImpl connection) { this.connection = connection; } public Statement getDelegate() { return delegate; } public void setDelegate(Statement delegate) { this.delegate = delegate; } } ================================================ FILE: bytejta-supports/src/main/java/org/bytesoft/bytejta/supports/resource/jdbc/XAConnectionImpl.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.resource.jdbc; import java.sql.Connection; import java.sql.PreparedStatement; 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.StatementEvent; import javax.sql.StatementEventListener; import javax.sql.XAConnection; import javax.transaction.xa.XAResource; import org.bytesoft.bytejta.supports.resource.CommonResourceDescriptor; import org.bytesoft.transaction.supports.resource.XAResourceDescriptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class XAConnectionImpl implements XAConnection, ConnectionEventListener, StatementEventListener { static final Logger logger = LoggerFactory.getLogger(XAConnectionImpl.class); private final Set connectionEventListeners = new HashSet(); private final Set statementEventListeners = new HashSet(); private String identifier; private XAConnection delegate; private boolean closed; private XAResource xaResource; public void statementClosed(StatementEvent event) { Iterator itr = this.statementEventListeners.iterator(); while (itr.hasNext()) { StatementEventListener listener = itr.next(); SQLException sqlException = event.getSQLException(); PreparedStatement statement = event.getStatement(); StatementEvent statementEvent = new StatementEvent(this, statement, sqlException); try { listener.statementClosed(statementEvent); } catch (RuntimeException error) { logger.warn("Error occurred!", error); } } // end-while (itr.hasNext()) } public void statementErrorOccurred(StatementEvent event) { Iterator itr = this.statementEventListeners.iterator(); while (itr.hasNext()) { StatementEventListener listener = itr.next(); SQLException sqlException = event.getSQLException(); PreparedStatement statement = event.getStatement(); StatementEvent statementEvent = new StatementEvent(this, statement, sqlException); try { listener.statementErrorOccurred(statementEvent); } catch (RuntimeException error) { logger.warn("Error occurred!", error); } } // end-while (itr.hasNext()) } public void connectionClosed(ConnectionEvent event) { Iterator itr = this.connectionEventListeners.iterator(); while (itr.hasNext()) { ConnectionEventListener listener = itr.next(); SQLException sqlException = event.getSQLException(); ConnectionEvent connectionEvent = new ConnectionEvent(this, sqlException); try { listener.connectionClosed(connectionEvent); } catch (RuntimeException error) { logger.warn("Error occurred!", error); } } // end-while (itr.hasNext()) } public void connectionErrorOccurred(ConnectionEvent event) { Iterator itr = this.connectionEventListeners.iterator(); while (itr.hasNext()) { ConnectionEventListener listener = itr.next(); SQLException sqlException = event.getSQLException(); ConnectionEvent connectionEvent = new ConnectionEvent(this, sqlException); try { listener.connectionErrorOccurred(connectionEvent); } catch (RuntimeException error) { logger.warn("Error occurred!", error); } } // end-while (itr.hasNext()) } public Connection getConnection() throws SQLException { Connection delegateConnection = this.delegate.getConnection(); ConnectionImpl connection = new ConnectionImpl(); connection.setManagedConnection(this); connection.setDelegate(delegateConnection); return connection; } public void addConnectionEventListener(ConnectionEventListener listener) { this.connectionEventListeners.add(listener); } public void removeConnectionEventListener(ConnectionEventListener listener) { this.connectionEventListeners.remove(listener); } public void addStatementEventListener(StatementEventListener listener) { this.statementEventListeners.add(listener); } public void removeStatementEventListener(StatementEventListener listener) { this.statementEventListeners.remove(listener); } public XAResource getXAResource() throws SQLException { if (this.xaResource == null) { this.initXAResourceIfNecessary(); } // end-if (this.xaResource == null) return this.xaResource; } private synchronized void initXAResourceIfNecessary() throws SQLException { if (this.xaResource == null) { XAResource delegateResource = this.delegate.getXAResource(); if (XAResourceDescriptor.class.isInstance(delegateResource)) { this.xaResource = delegateResource; } else { CommonResourceDescriptor descriptor = new CommonResourceDescriptor(); descriptor.setDelegate(delegateResource); descriptor.setIdentifier(this.identifier); this.xaResource = descriptor; } } // end-if (this.xaResource == null) } public void close() throws SQLException { if (this.closed == false) { this.delegate.close(); this.closed = true; } // end-if (this.closed == false) } public String getIdentifier() { return identifier; } public void setIdentifier(String identifier) { this.identifier = identifier; } public XAConnection getDelegate() { return delegate; } public void setDelegate(XAConnection delegate) { this.delegate = delegate; } } ================================================ FILE: bytejta-supports/src/main/java/org/bytesoft/bytejta/supports/resource/jdbc/XADataSourceImpl.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.resource.jdbc; import java.io.PrintWriter; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.util.logging.Logger; import javax.sql.XAConnection; import javax.sql.XADataSource; import org.springframework.beans.factory.BeanNameAware; public class XADataSourceImpl implements XADataSource, BeanNameAware { private String identifier; private XADataSource xaDataSource; public PrintWriter getLogWriter() throws SQLException { return this.xaDataSource.getLogWriter(); } public void setLogWriter(PrintWriter out) throws SQLException { this.xaDataSource.setLogWriter(out); } public void setLoginTimeout(int seconds) throws SQLException { this.xaDataSource.setLoginTimeout(seconds); } public int getLoginTimeout() throws SQLException { return this.xaDataSource.getLoginTimeout(); } public Logger getParentLogger() throws SQLFeatureNotSupportedException { return this.xaDataSource.getParentLogger(); } public XAConnection getXAConnection() throws SQLException { XAConnection delegate = this.xaDataSource.getXAConnection(); XAConnectionImpl managed = new XAConnectionImpl(); managed.setIdentifier(this.identifier); managed.setDelegate(delegate); delegate.addConnectionEventListener(managed); delegate.addStatementEventListener(managed); return managed; } public XAConnection getXAConnection(String user, String password) throws SQLException { XAConnection delegate = this.xaDataSource.getXAConnection(user, password); XAConnectionImpl managed = new XAConnectionImpl(); managed.setIdentifier(this.identifier); managed.setDelegate(delegate); delegate.addConnectionEventListener(managed); delegate.addStatementEventListener(managed); return managed; } public void setBeanName(String name) { this.setIdentifier(name); } public String getIdentifier() { return identifier; } public void setIdentifier(String identifier) { this.identifier = identifier; } public XADataSource getXaDataSource() { return xaDataSource; } public void setXaDataSource(XADataSource xaDataSource) { this.xaDataSource = xaDataSource; } } ================================================ FILE: bytejta-supports/src/main/java/org/bytesoft/bytejta/supports/resource/properties/ConnectorResourcePropertySource.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.bytejta.supports.resource.properties; import java.util.HashMap; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.springframework.core.env.PropertySource; import org.springframework.core.io.AbstractResource; import org.springframework.core.io.support.EncodedResource; public class ConnectorResourcePropertySource extends PropertySource { private final Map aliases = new HashMap(); private boolean enabled; public ConnectorResourcePropertySource(String name, EncodedResource source) { this(name, source, new HashMap()); } public ConnectorResourcePropertySource(String name, EncodedResource source, Map aliases) { super(name, source); this.aliases.putAll(aliases); EncodedResource encoded = (EncodedResource) this.getSource(); AbstractResource resource = (AbstractResource) encoded.getResource(); String path = resource.getFilename(); if (StringUtils.isBlank(path)) { return; } String[] values = path.split(":"); if (values.length != 2) { return; } String protocol = values[0]; String resName = values[1]; if ("bytejta".equalsIgnoreCase(protocol) == false) { return; } else if ("connector.config".equalsIgnoreCase(resName) == false) { return; } this.enabled = true; } public Object getProperty(String name) { if (this.enabled == false) { return null; } else if (StringUtils.isBlank(name)) { return null; } else if (StringUtils.startsWith(name, "spring.datasource.") == false) { return null; } int dotIndex = name.lastIndexOf("."); String prefix = name.substring(0, dotIndex); String suffix = name.substring(dotIndex + 1); String alias = this.aliases.get(suffix); if (StringUtils.isBlank(alias)) { return null; } return String.format("${%s.%s}", prefix, alias); } } ================================================ FILE: bytejta-supports/src/main/java/org/bytesoft/bytejta/supports/resource/properties/ConnectorResourcePropertySourceFactory.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.bytejta.supports.resource.properties; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.springframework.core.env.PropertySource; import org.springframework.core.io.support.EncodedResource; import org.springframework.core.io.support.PropertySourceFactory; public class ConnectorResourcePropertySourceFactory implements PropertySourceFactory { static final Map aliases = new HashMap(); static { aliases.put("user", "username"); aliases.put("jdbcUrl", "url"); } public static Map getAliases() { return aliases; } public PropertySource createPropertySource(String name, EncodedResource resource) throws IOException { if (name == null) { name = String.format("%s@%s", ConnectorResourcePropertySource.class, System.identityHashCode(resource)); } // end-if (name == null) return new ConnectorResourcePropertySource(name, resource, aliases); } } ================================================ FILE: bytejta-supports/src/main/java/org/bytesoft/bytejta/supports/rpc/TransactionInterceptorImpl.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.rpc; import javax.transaction.RollbackException; import javax.transaction.Status; import javax.transaction.SystemException; import javax.transaction.xa.XAException; import javax.transaction.xa.XAResource; import org.bytesoft.bytejta.supports.resource.RemoteResourceDescriptor; import org.bytesoft.transaction.Transaction; import org.bytesoft.transaction.TransactionBeanFactory; import org.bytesoft.transaction.TransactionContext; import org.bytesoft.transaction.TransactionManager; import org.bytesoft.transaction.TransactionParticipant; import org.bytesoft.transaction.aware.TransactionBeanFactoryAware; import org.bytesoft.transaction.remote.RemoteCoordinator; import org.bytesoft.transaction.supports.rpc.TransactionInterceptor; import org.bytesoft.transaction.supports.rpc.TransactionRequest; import org.bytesoft.transaction.supports.rpc.TransactionResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class TransactionInterceptorImpl implements TransactionInterceptor, TransactionBeanFactoryAware { static final Logger logger = LoggerFactory.getLogger(TransactionInterceptorImpl.class); @javax.inject.Inject protected TransactionBeanFactory beanFactory; public void beforeSendRequest(TransactionRequest request) throws IllegalStateException { TransactionManager transactionManager = this.beanFactory.getTransactionManager(); Transaction transaction = transactionManager.getTransactionQuietly(); if (transaction == null) { return; } if (transaction.getTransactionStatus() == Status.STATUS_MARKED_ROLLBACK) { throw new IllegalStateException( "Transaction has been marked as rollback only, can not propagate its context to remote branch."); } // end-if (transaction.getTransactionStatus() == Status.STATUS_MARKED_ROLLBACK) TransactionContext srcTransactionContext = transaction.getTransactionContext(); TransactionContext transactionContext = srcTransactionContext.clone(); request.setTransactionContext(transactionContext); try { RemoteCoordinator resource = request.getTargetTransactionCoordinator(); RemoteResourceDescriptor descriptor = new RemoteResourceDescriptor(); descriptor.setDelegate(resource); boolean participantEnlisted = transaction.enlistResource(descriptor); ((TransactionRequestImpl) request).setParticipantEnlistFlag(participantEnlisted); } catch (IllegalStateException ex) { logger.error("TransactionInterceptorImpl.beforeSendRequest(TransactionRequest)", ex); throw ex; } catch (RollbackException ex) { transaction.setRollbackOnlyQuietly(); logger.error("TransactionInterceptorImpl.beforeSendRequest(TransactionRequest)", ex); throw new IllegalStateException(ex); } catch (SystemException ex) { logger.error("TransactionInterceptorImpl.beforeSendRequest(TransactionRequest)", ex); throw new IllegalStateException(ex); } } public void beforeSendResponse(TransactionResponse response) throws IllegalStateException { TransactionManager transactionManager = this.beanFactory.getTransactionManager(); Transaction transaction = transactionManager.getTransactionQuietly(); if (transaction == null) { return; } TransactionParticipant coordinator = this.beanFactory.getNativeParticipant(); TransactionContext srcTransactionContext = transaction.getTransactionContext(); TransactionContext transactionContext = srcTransactionContext.clone(); transactionContext.setPropagatedBy(srcTransactionContext.getPropagatedBy()); response.setTransactionContext(transactionContext); // response.setSourceTransactionCoordinator(coordinator); try { coordinator.end(transactionContext, XAResource.TMSUCCESS); } catch (XAException ex) { throw new IllegalStateException(ex); } } public void afterReceiveRequest(TransactionRequest request) throws IllegalStateException { TransactionContext srcTransactionContext = request.getTransactionContext(); if (srcTransactionContext == null) { return; } TransactionParticipant coordinator = this.beanFactory.getNativeParticipant(); TransactionContext transactionContext = srcTransactionContext.clone(); transactionContext.setPropagatedBy(srcTransactionContext.getPropagatedBy()); try { coordinator.start(transactionContext, XAResource.TMNOFLAGS); } catch (XAException ex) { throw new IllegalStateException(ex); } } public void afterReceiveResponse(TransactionResponse response) throws IllegalStateException { TransactionManager transactionManager = this.beanFactory.getTransactionManager(); Transaction transaction = transactionManager.getTransactionQuietly(); TransactionContext transactionContext = response.getTransactionContext(); RemoteCoordinator resource = response.getSourceTransactionCoordinator(); boolean participantEnlistFlag = ((TransactionResponseImpl) response).isParticipantEnlistFlag(); // boolean participantDelistFlag = ((TransactionResponseImpl) response).isParticipantDelistFlag(); if (transaction == null || transactionContext == null) { return; } else if (participantEnlistFlag == false) { return; } else if (resource == null) { logger.error("TransactionInterceptorImpl.afterReceiveResponse(TransactionRequest): remote coordinator is null."); throw new IllegalStateException("remote coordinator is null."); } try { RemoteResourceDescriptor descriptor = new RemoteResourceDescriptor(); descriptor.setDelegate(resource); // descriptor.setIdentifier(resource.getIdentifier()); transaction.delistResource(descriptor, XAResource.TMSUCCESS); // transaction.delistResource(descriptor, participantDelistFlag ? XAResource.TMFAIL : XAResource.TMSUCCESS); } catch (IllegalStateException ex) { logger.error("TransactionInterceptorImpl.afterReceiveResponse(TransactionRequest)", ex); throw ex; } catch (SystemException ex) { logger.error("TransactionInterceptorImpl.afterReceiveResponse(TransactionRequest)", ex); throw new IllegalStateException(ex); } } public TransactionBeanFactory getBeanFactory() { return this.beanFactory; } public void setBeanFactory(TransactionBeanFactory tbf) { this.beanFactory = tbf; } } ================================================ FILE: bytejta-supports/src/main/java/org/bytesoft/bytejta/supports/rpc/TransactionRequestImpl.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.rpc; import java.util.HashMap; import java.util.Map; import org.bytesoft.transaction.TransactionContext; import org.bytesoft.transaction.remote.RemoteCoordinator; import org.bytesoft.transaction.supports.rpc.TransactionRequest; public class TransactionRequestImpl implements TransactionRequest { private RemoteCoordinator participantCoordinator; private TransactionContext transactionContext; private transient boolean participantEnlistFlag; private final Map headers = new HashMap(); public Object getHeader(String name) { return this.headers.get(name); } public void setHeader(String name, Object value) { this.headers.put(name, value); } public RemoteCoordinator getTargetTransactionCoordinator() { return this.participantCoordinator; } public void setTargetTransactionCoordinator(RemoteCoordinator remoteCoordinator) { this.participantCoordinator = remoteCoordinator; } public TransactionContext getTransactionContext() { return this.transactionContext; } public void setTransactionContext(TransactionContext transactionContext) { this.transactionContext = transactionContext; } public boolean isParticipantEnlistFlag() { return participantEnlistFlag; } public void setParticipantEnlistFlag(boolean participantEnlistFlag) { this.participantEnlistFlag = participantEnlistFlag; } } ================================================ FILE: bytejta-supports/src/main/java/org/bytesoft/bytejta/supports/rpc/TransactionResponseImpl.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.rpc; import java.util.HashMap; import java.util.Map; import org.bytesoft.transaction.TransactionContext; import org.bytesoft.transaction.remote.RemoteCoordinator; import org.bytesoft.transaction.supports.rpc.TransactionResponse; public class TransactionResponseImpl implements TransactionResponse { private boolean participantStickyRequired = true; private boolean participantRollbackOnly; private RemoteCoordinator participantCoordinator; private TransactionContext transactionContext; private transient boolean participantEnlistFlag; private transient boolean participantDelistFlag; private final Map headers = new HashMap(); public Object getHeader(String name) { return this.headers.get(name); } public void setHeader(String name, Object value) { this.headers.put(name, value); } public RemoteCoordinator getSourceTransactionCoordinator() { return this.participantCoordinator; } public void setSourceTransactionCoordinator(RemoteCoordinator remoteCoordinator) { this.participantCoordinator = remoteCoordinator; } public TransactionContext getTransactionContext() { return this.transactionContext; } public void setTransactionContext(TransactionContext transactionContext) { this.transactionContext = transactionContext; } public boolean isParticipantRollbackOnly() { return participantRollbackOnly; } public void setParticipantRollbackOnly(boolean participantRollbackOnly) { this.participantRollbackOnly = participantRollbackOnly; } public boolean isParticipantStickyRequired() { return participantStickyRequired; } public void setParticipantStickyRequired(boolean participantStickyRequired) { this.participantStickyRequired = participantStickyRequired; } public boolean isParticipantDelistFlag() { return participantDelistFlag; } public void setParticipantDelistFlag(boolean participantDelistFlag) { this.participantDelistFlag = participantDelistFlag; } public boolean isParticipantEnlistFlag() { return participantEnlistFlag; } public void setParticipantEnlistFlag(boolean participantEnlistFlag) { this.participantEnlistFlag = participantEnlistFlag; } } ================================================ FILE: bytejta-supports/src/main/java/org/bytesoft/bytejta/supports/serialize/XAResourceDeserializerImpl.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.bytejta.supports.serialize; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import javax.jms.XAConnectionFactory; import javax.jms.XASession; import javax.resource.spi.ManagedConnection; import javax.resource.spi.ManagedConnectionFactory; import javax.sql.XAConnection; import javax.sql.XADataSource; import javax.transaction.xa.XAResource; import org.bytesoft.bytejta.supports.jdbc.DataSourceHolder; import org.bytesoft.bytejta.supports.jdbc.RecoveredResource; import org.bytesoft.bytejta.supports.resource.CommonResourceDescriptor; import org.bytesoft.bytejta.supports.resource.LocalXAResourceDescriptor; import org.bytesoft.transaction.supports.resource.XAResourceDescriptor; import org.bytesoft.transaction.supports.serialize.XAResourceDeserializer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; public class XAResourceDeserializerImpl implements XAResourceDeserializer, ApplicationContextAware { static final Logger logger = LoggerFactory.getLogger(XAResourceDeserializerImpl.class); private Map cachedResourceMap = new ConcurrentHashMap(); private ApplicationContext applicationContext; public XAResourceDescriptor deserialize(String identifier) { XAResourceDescriptor cachedResource = this.cachedResourceMap.get(identifier); if (cachedResource != null) { return cachedResource; } try { Object bean = this.applicationContext.getBean(identifier); XAResourceDescriptor resolvedResource = this.deserializeResource(identifier, bean); if (resolvedResource == null) { logger.error("can not find a matching xa-resource(beanId= {})!", identifier); return null; } this.cachedResourceMap.put(identifier, resolvedResource); return resolvedResource; } catch (BeansException bex) { logger.error("can not find a matching xa-resource(beanId= {})!", identifier); return null; } catch (Exception ex) { logger.error("can not find a matching xa-resource(beanId= {})!", identifier, ex); return null; } } private XAResourceDescriptor deserializeResource(String identifier, Object bean) throws Exception { if (DataSourceHolder.class.isInstance(bean)) { DataSourceHolder holder = (DataSourceHolder) bean; RecoveredResource xares = new RecoveredResource(); xares.setDataSource(holder.getDataSource()); LocalXAResourceDescriptor descriptor = new LocalXAResourceDescriptor(); descriptor.setDelegate(xares); descriptor.setIdentifier(identifier); return descriptor; } else if (javax.sql.DataSource.class.isInstance(bean)) { javax.sql.DataSource dataSource = (javax.sql.DataSource) bean; RecoveredResource xares = new RecoveredResource(); xares.setDataSource(dataSource); LocalXAResourceDescriptor descriptor = new LocalXAResourceDescriptor(); descriptor.setDelegate(xares); descriptor.setIdentifier(identifier); return descriptor; } else if (XADataSource.class.isInstance(bean)) { XADataSource xaDataSource = (XADataSource) bean; XAConnection xaConnection = xaDataSource.getXAConnection(); java.sql.Connection connection = null; try { connection = xaConnection.getConnection(); XAResource xares = xaConnection.getXAResource(); CommonResourceDescriptor descriptor = new CommonResourceDescriptor(); descriptor.setDelegate(xares); descriptor.setIdentifier(identifier); descriptor.setManaged(xaConnection); return descriptor; } catch (Exception ex) { logger.warn(ex.getMessage(), ex); XAResource xares = xaConnection.getXAResource(); CommonResourceDescriptor descriptor = new CommonResourceDescriptor(); descriptor.setDelegate(xares); descriptor.setIdentifier(identifier); descriptor.setManaged(xaConnection); return descriptor; } finally { this.closeQuietly(connection); } } else if (XAConnectionFactory.class.isInstance(bean)) { XAConnectionFactory connectionFactory = (XAConnectionFactory) bean; javax.jms.XAConnection xaConnection = connectionFactory.createXAConnection(); XASession xaSession = xaConnection.createXASession(); javax.jms.Session session = null; try { session = xaSession.getSession(); XAResource xares = xaSession.getXAResource(); CommonResourceDescriptor descriptor = new CommonResourceDescriptor(); descriptor.setDelegate(xares); descriptor.setIdentifier(identifier); descriptor.setManaged(xaConnection); return descriptor; } catch (Exception ex) { logger.warn(ex.getMessage(), ex); XAResource xares = xaSession.getXAResource(); CommonResourceDescriptor descriptor = new CommonResourceDescriptor(); descriptor.setDelegate(xares); descriptor.setIdentifier(identifier); descriptor.setManaged(xaConnection); return descriptor; } finally { this.closeQuietly(session); } } else if (ManagedConnectionFactory.class.isInstance(bean)) { ManagedConnectionFactory connectionFactory = (ManagedConnectionFactory) bean; ManagedConnection managedConnection = connectionFactory.createManagedConnection(null, null); javax.resource.cci.Connection connection = null; try { connection = (javax.resource.cci.Connection) managedConnection.getConnection(null, null); XAResource xares = managedConnection.getXAResource(); CommonResourceDescriptor descriptor = new CommonResourceDescriptor(); descriptor.setDelegate(xares); descriptor.setIdentifier(identifier); descriptor.setManaged(managedConnection); return descriptor; } catch (Exception ex) { logger.warn(ex.getMessage(), ex); XAResource xares = managedConnection.getXAResource(); CommonResourceDescriptor descriptor = new CommonResourceDescriptor(); descriptor.setDelegate(xares); descriptor.setIdentifier(identifier); descriptor.setManaged(managedConnection); return descriptor; } finally { this.closeQuietly(connection); } } else { return null; } } protected void closeQuietly(javax.resource.cci.Connection closeable) { if (closeable != null) { try { closeable.close(); } catch (Exception ex) { logger.debug(ex.getMessage()); } } } protected void closeQuietly(java.sql.Connection closeable) { if (closeable != null) { try { closeable.close(); } catch (Exception ex) { logger.debug(ex.getMessage()); } } } protected void closeQuietly(javax.jms.Session closeable) { if (closeable != null) { try { closeable.close(); } catch (Exception ex) { logger.debug(ex.getMessage()); } } } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } public ApplicationContext getApplicationContext() { return applicationContext; } } ================================================ FILE: bytejta-supports/src/main/java/org/bytesoft/bytejta/supports/spring/ManagedConnectionFactoryPostProcessor.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.spring; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.util.Iterator; import java.util.Map; import javax.jms.XAConnectionFactory; import javax.resource.spi.ManagedConnectionFactory; import javax.sql.XADataSource; import javax.transaction.TransactionManager; import org.apache.commons.dbcp2.managed.BasicManagedDataSource; import org.bytesoft.bytejta.TransactionBeanFactoryImpl; import org.bytesoft.bytejta.supports.jdbc.LocalXADataSource; import org.bytesoft.bytejta.supports.resource.ManagedConnectionFactoryHandler; import org.bytesoft.bytejta.supports.resource.jdbc.XADataSourceImpl; import org.bytesoft.transaction.TransactionBeanFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.core.Ordered; public class ManagedConnectionFactoryPostProcessor implements BeanPostProcessor, Ordered, SmartInitializingSingleton, ApplicationContextAware { static String BEAN_TRANSACTION_MANAGER = "transactionManager"; private ApplicationContext applicationContext; public void afterSingletonsInstantiated() { Map beanMap = this.applicationContext.getBeansOfType(LocalXADataSource.class); Iterator> iterator = beanMap == null ? null : beanMap.entrySet().iterator(); while (iterator != null && iterator.hasNext()) { Map.Entry entry = iterator.next(); LocalXADataSource bean = entry.getValue(); this.initializeTransactionManagerIfNecessary(bean); } } private void initializeTransactionManagerIfNecessary(LocalXADataSource target) { if (target.getTransactionManager() == null) { TransactionManager transactionManager = // (TransactionManager) this.applicationContext.getBean(BEAN_TRANSACTION_MANAGER); target.setTransactionManager(transactionManager); } // end-if (target.getTransactionManager() == null) } public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return this.wrapManagedConnectionFactoryIfNecessary(bean, beanName); } public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return this.wrapManagedConnectionFactoryIfNecessary(bean, beanName); } private Object wrapManagedConnectionFactoryIfNecessary(Object bean, String beanName) throws BeansException { Class clazz = bean.getClass(); ClassLoader cl = clazz.getClassLoader(); Class[] interfaces = clazz.getInterfaces(); if (this.hasAlreadyBeenWrappedBySelf(bean)) /* managed-connection-factory has already been wrapped */ { return bean; } else if (LocalXADataSource.class.isInstance(bean)) { LocalXADataSource target = (LocalXADataSource) bean; this.initializeTransactionManagerIfNecessary(target); return bean; } else if (BasicManagedDataSource.class.isInstance(bean)) /* spring boot auto configuration */ { BasicManagedDataSource managedDataSource = (BasicManagedDataSource) bean; TransactionBeanFactory beanFactory = TransactionBeanFactoryImpl.getInstance(); managedDataSource.setTransactionManager(beanFactory.getTransactionManager()); return bean; } else if (XADataSource.class.isInstance(bean)) { XADataSource xaDataSource = (XADataSource) bean; XADataSourceImpl wrappedDataSource = new XADataSourceImpl(); wrappedDataSource.setIdentifier(beanName); wrappedDataSource.setXaDataSource(xaDataSource); return wrappedDataSource; } else if (XAConnectionFactory.class.isInstance(bean)) { ManagedConnectionFactoryHandler interceptor = new ManagedConnectionFactoryHandler(bean); interceptor.setIdentifier(beanName); return Proxy.newProxyInstance(cl, interfaces, interceptor); } else if (ManagedConnectionFactory.class.isInstance(bean)) { ManagedConnectionFactoryHandler interceptor = new ManagedConnectionFactoryHandler(bean); interceptor.setIdentifier(beanName); return Proxy.newProxyInstance(cl, interfaces, interceptor); } else { return bean; } } private boolean hasAlreadyBeenWrappedBySelf(Object bean) { if (XADataSourceImpl.class.isInstance(bean)) { return true; } if (Proxy.isProxyClass(bean.getClass()) == false) { return false; } InvocationHandler handler = Proxy.getInvocationHandler(bean); return ManagedConnectionFactoryHandler.class.isInstance(handler); } public int getOrder() { return Ordered.LOWEST_PRECEDENCE; } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } } ================================================ FILE: bytejta-supports/src/main/java/org/bytesoft/bytejta/supports/spring/TransactionBeanFactoryAutoInjector.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.spring; import java.util.Iterator; import java.util.Map; import org.bytesoft.transaction.TransactionBeanFactory; import org.bytesoft.transaction.aware.TransactionBeanFactoryAware; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.core.Ordered; @org.springframework.core.annotation.Order(org.springframework.core.Ordered.HIGHEST_PRECEDENCE) public class TransactionBeanFactoryAutoInjector implements BeanPostProcessor, Ordered, SmartInitializingSingleton, ApplicationContextAware { static final Logger logger = LoggerFactory.getLogger(TransactionBeanFactoryAutoInjector.class); private ApplicationContext applicationContext; public void afterSingletonsInstantiated() { Map beanMap = // this.applicationContext.getBeansOfType(TransactionBeanFactoryAware.class); Iterator> iterator = // (beanMap == null) ? null : beanMap.entrySet().iterator(); while (iterator != null && iterator.hasNext()) { Map.Entry entry = iterator.next(); TransactionBeanFactoryAware bean = entry.getValue(); this.initializeTransactionBeanFactoryIfNecessary(bean); } } public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (TransactionBeanFactoryAware.class.isInstance(bean)) { this.initializeTransactionBeanFactoryIfNecessary((TransactionBeanFactoryAware) bean); } // end-if (TransactionBeanFactoryAware.class.isInstance(bean)) return bean; } private void initializeTransactionBeanFactoryIfNecessary(TransactionBeanFactoryAware aware) { if (aware.getBeanFactory() == null) { TransactionBeanFactory beanFactory = // this.applicationContext.getBean(TransactionBeanFactory.class); aware.setBeanFactory(beanFactory); } // end-if (aware.getBeanFactory() == null) } public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } } ================================================ FILE: bytejta-supports/src/main/java/org/bytesoft/bytejta/supports/spring/TransactionDebuggablePostProcessor.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.bytejta.supports.spring; import org.apache.commons.lang3.StringUtils; import org.bytesoft.transaction.aware.TransactionDebuggable; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.EnvironmentAware; import org.springframework.core.env.Environment; public class TransactionDebuggablePostProcessor implements BeanPostProcessor, EnvironmentAware { static final String KEY_DEBUGGABLE = "org.bytesoft.bytejta.debuggable"; private Environment environment; public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { this.setDebuggableIfNecessary(bean); return bean; } public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { this.setDebuggableIfNecessary(bean); return bean; } private void setDebuggableIfNecessary(Object bean) { if (TransactionDebuggable.class.isInstance(bean)) { String debuggable = this.environment.getProperty(KEY_DEBUGGABLE); TransactionDebuggable target = (TransactionDebuggable) bean; target.setDebuggingEnabled(StringUtils.equalsIgnoreCase(Boolean.TRUE.toString(), debuggable)); } } public void setEnvironment(Environment environment) { this.environment = environment; } } ================================================ FILE: bytejta-supports/src/main/resources/bytejta-supports-core.xml ================================================ The bytejta transaction manager module ================================================ FILE: bytejta-supports/src/main/resources/bytejta-supports-standalone.xml ================================================ ================================================ FILE: bytejta-supports/src/main/resources/bytejta-supports-task.xml ================================================ ================================================ FILE: bytejta-supports-dubbo/pom.xml ================================================ 4.0.0 org.bytesoft bytejta-parent 0.5.0-BETA9 bytejta-supports-dubbo jar bytejta-supports-dubbo http://www.bytesoft.org UTF-8 org.bytesoft bytejta-supports com.alibaba dubbo org.springframework spring org.springframework.boot spring-boot org.springframework.boot spring-boot-autoconfigure ================================================ FILE: bytejta-supports-dubbo/src/main/java/org/bytesoft/bytejta/supports/dubbo/DubboRemoteCoordinator.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.dubbo; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import javax.transaction.xa.XAException; import javax.transaction.xa.XAResource; import org.apache.commons.lang3.StringUtils; import org.bytesoft.bytejta.supports.internal.RemoteCoordinatorRegistry; import org.bytesoft.common.utils.CommonUtils; import org.bytesoft.transaction.TransactionParticipant; import org.bytesoft.transaction.remote.RemoteAddr; import org.bytesoft.transaction.remote.RemoteCoordinator; import org.bytesoft.transaction.remote.RemoteNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DubboRemoteCoordinator implements InvocationHandler { public static final int KEY_PARTICIPANT_TYPE_GLOBAL = 0; public static final int KEY_PARTICIPANT_TYPE_SYSTEM = 1; public static final int KEY_PARTICIPANT_TYPE_EXACT = 2; static final Logger logger = LoggerFactory.getLogger(DubboRemoteCoordinator.class); private RemoteCoordinator remoteCoordinator; private int coordinatorType; // 0, global; 1, system; 2, direct private RemoteNode invocationContext; private RemoteCoordinator proxyCoordinator; public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { RemoteCoordinatorRegistry participantRegistry = RemoteCoordinatorRegistry.getInstance(); InvocationContextRegistry invocationRegistry = InvocationContextRegistry.getInstance(); try { invocationRegistry.associateInvocationContext(this.invocationContext); Class clazz = method.getDeclaringClass(); String methodName = method.getName(); Class returnType = method.getReturnType(); if (Object.class.equals(clazz)) { return method.invoke(this, args); } else if (TransactionParticipant.class.equals(clazz)) { throw new XAException(XAException.XAER_RMFAIL); } else if (RemoteCoordinator.class.equals(clazz)) { if ("getIdentifier".equals(methodName)) { return this.getParticipantsIdentifier(proxy, method, args); } else if ("getApplication".equals(methodName)) { return this.getParticipantsApplication(proxy, method, args); } else if ("getRemoteAddr".equals(methodName) && RemoteAddr.class.equals(returnType)) { String identifier = this.getParticipantsIdentifier(proxy, method, args); return identifier == null ? null : CommonUtils.getRemoteAddr(identifier); } else if ("getRemoteNode".equals(methodName) && RemoteNode.class.equals(returnType)) { String identifier = this.getParticipantsIdentifier(proxy, method, args); return identifier == null ? null : CommonUtils.getRemoteNode(identifier); } else { throw new XAException(XAException.XAER_RMFAIL); } } else if (XAResource.class.equals(clazz)) { String serverHost = this.invocationContext == null ? null : this.invocationContext.getServerHost(); int serverPort = this.invocationContext == null ? 0 : this.invocationContext.getServerPort(); String remoteText = String.format("%s:%s:%s", serverHost, null, serverPort); RemoteAddr remoteAddr = CommonUtils.getRemoteAddr(remoteText); if ("start".equals(methodName)) { if (this.invocationContext == null) { throw new IllegalAccessException(); } else if (participantRegistry.getRemoteNode(remoteAddr) != null) { return null; } else { return this.invokeForGeneric(proxy, method, args); } } else if ("prepare".equals(methodName)) { return this.invokeForGeneric(proxy, method, args); } else if ("commit".equals(methodName)) { return this.invokeForGeneric(proxy, method, args); } else if ("rollback".equals(methodName)) { return this.invokeForGeneric(proxy, method, args); } else if ("recover".equals(methodName)) { return this.invokeForGeneric(proxy, method, args); } else if ("forget".equals(methodName)) { return this.invokeForGeneric(proxy, method, args); } else { throw new XAException(XAException.XAER_RMFAIL); } } else { throw new IllegalAccessException(); } } finally { invocationRegistry.desociateInvocationContext(); } } private String getParticipantsIdentifier(Object proxy, Method method, Object[] args) throws Throwable { if (this.invocationContext == null) { return null; } String serverHost = this.invocationContext == null ? null : this.invocationContext.getServerHost(); String serviceKey = this.invocationContext == null ? null : this.invocationContext.getServiceKey(); int serverPort = this.invocationContext == null ? 0 : this.invocationContext.getServerPort(); if (StringUtils.isNotBlank(serviceKey) && StringUtils.equalsIgnoreCase(serviceKey, "null") == false) { return String.format("%s:%s:%s", serverHost, serviceKey, serverPort); } else { Object application = this.getParticipantsApplication(proxy, method, args); return String.format("%s:%s:%s", serverHost, application, serverPort); } } private Object getParticipantsApplication(Object proxy, Method method, Object[] args) throws Throwable { if (this.invocationContext == null) { return null; } else if (StringUtils.isNotBlank(this.invocationContext.getServiceKey())) { return this.invocationContext.getServiceKey(); } RemoteCoordinatorRegistry participantRegistry = RemoteCoordinatorRegistry.getInstance(); String serverHost = this.invocationContext.getServerHost(); int serverPort = this.invocationContext.getServerPort(); RemoteAddr remoteAddr = new RemoteAddr(); remoteAddr.setServerHost(serverHost); remoteAddr.setServerPort(serverPort); RemoteNode remoteNode = participantRegistry.getRemoteNode(remoteAddr); if (remoteNode != null) { this.invocationContext.setServiceKey(remoteNode.getServiceKey()); return this.invocationContext.getServiceKey(); } Object result = this.invokeForSpecifiedDestination(proxy, method, args); String application = CommonUtils.getApplication(String.valueOf(result)); if (StringUtils.isBlank(application) || StringUtils.equals(application, "null")) { return null; } this.invocationContext.setServiceKey(application); if (participantRegistry.containsRemoteNode(remoteAddr) == false) { RemoteNode targetNode = new RemoteNode(); targetNode.setServerHost(serverHost); targetNode.setServiceKey(application); targetNode.setServerPort(serverPort); participantRegistry.putRemoteNode(remoteAddr, targetNode); } return application; } public Object invokeForGeneric(Object proxy, Method method, Object[] args) throws Throwable { try { return method.invoke(this.remoteCoordinator, args); } catch (IllegalArgumentException error) { logger.warn("Error occurred!", error); throw new XAException(XAException.XAER_RMERR); } catch (InvocationTargetException error) { throw error.getTargetException(); } catch (IllegalAccessException error) { logger.warn("Error occurred!", error); throw new XAException(XAException.XAER_RMERR); } } public Object invokeForSpecifiedDestination(Object proxy, Method method, Object[] args) throws Throwable { RemoteCoordinatorRegistry participantRegistry = RemoteCoordinatorRegistry.getInstance(); String serverHost = this.invocationContext.getServerHost(); int serverPort = this.invocationContext.getServerPort(); String address = String.format("%s:%s:%s", serverHost, null, serverPort); RemoteAddr remoteAddr = CommonUtils.getRemoteAddr(address); RemoteCoordinator participant = participantRegistry.getPhysicalInstance(remoteAddr); if (participant == null) { throw new XAException(XAException.XAER_RMERR); } try { return method.invoke(participant, args); } catch (IllegalArgumentException error) { logger.warn("Error occurred!", error); throw new XAException(XAException.XAER_RMERR); } catch (InvocationTargetException error) { throw error.getTargetException(); } catch (IllegalAccessException error) { logger.warn("Error occurred!", error); throw new XAException(XAException.XAER_RMERR); } } public String toString() { return String.format("", this.invocationContext.getServerHost(), this.invocationContext.getServiceKey(), this.invocationContext.getServerPort()); } public RemoteNode getInvocationContext() { return invocationContext; } public void setInvocationContext(RemoteNode invocationContext) { this.invocationContext = invocationContext; } public int getCoordinatorType() { return coordinatorType; } public void setCoordinatorType(int coordinatorType) { this.coordinatorType = coordinatorType; } public RemoteCoordinator getProxyCoordinator() { return proxyCoordinator; } public void setProxyCoordinator(RemoteCoordinator proxyCoordinator) { this.proxyCoordinator = proxyCoordinator; } public RemoteCoordinator getRemoteCoordinator() { return remoteCoordinator; } public void setRemoteCoordinator(RemoteCoordinator remoteCoordinator) { this.remoteCoordinator = remoteCoordinator; } } ================================================ FILE: bytejta-supports-dubbo/src/main/java/org/bytesoft/bytejta/supports/dubbo/InvocationContextRegistry.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.dubbo; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.bytesoft.transaction.remote.RemoteNode; public final class InvocationContextRegistry { private static final InvocationContextRegistry instance = new InvocationContextRegistry(); private final Map contexts = new ConcurrentHashMap(); private InvocationContextRegistry() { if (instance != null) { throw new IllegalStateException(); } } public static InvocationContextRegistry getInstance() { return instance; } public void associateInvocationContext(RemoteNode context) { this.contexts.put(Thread.currentThread(), context); } public RemoteNode desociateInvocationContext() { return this.contexts.remove(Thread.currentThread()); } public RemoteNode getInvocationContext() { return this.contexts.get(Thread.currentThread()); } } ================================================ FILE: bytejta-supports-dubbo/src/main/java/org/bytesoft/bytejta/supports/dubbo/TransactionBeanRegistry.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.dubbo; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import org.bytesoft.transaction.TransactionBeanFactory; import org.bytesoft.transaction.aware.TransactionBeanFactoryAware; import org.bytesoft.transaction.remote.RemoteCoordinator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.EnvironmentAware; import org.springframework.core.env.Environment; public class TransactionBeanRegistry implements TransactionBeanFactoryAware, ApplicationContextAware, EnvironmentAware { static final Logger logger = LoggerFactory.getLogger(TransactionBeanRegistry.class); private static final TransactionBeanRegistry instance = new TransactionBeanRegistry(); private RemoteCoordinator consumeCoordinator; private ApplicationContext applicationContext; private Environment environment; @javax.inject.Inject private TransactionBeanFactory beanFactory; private Lock lock = new ReentrantLock(); private Condition condition = this.lock.newCondition(); private TransactionBeanRegistry() { if (instance != null) { throw new IllegalStateException(); } } public static TransactionBeanRegistry getInstance() { return instance; } public RemoteCoordinator getConsumeCoordinator() { if (this.consumeCoordinator != null) { return this.consumeCoordinator; } else { return this.doGetConsumeCoordinator(); } } private RemoteCoordinator doGetConsumeCoordinator() { try { this.lock.lock(); while (this.consumeCoordinator == null) { try { this.condition.await(1, TimeUnit.SECONDS); } catch (InterruptedException ex) { logger.debug(ex.getMessage()); } } // ConsumeCoordinator is injected by the TransactionConfigPostProcessor, which has a slight delay. return consumeCoordinator; } finally { this.lock.unlock(); } } public void setConsumeCoordinator(RemoteCoordinator consumeCoordinator) { try { this.lock.lock(); if (this.consumeCoordinator == null) { this.consumeCoordinator = consumeCoordinator; this.condition.signalAll(); } else { throw new IllegalStateException( "Field 'consumeCoordinator' has already been set, please check your app whether it imports ByteJTA repeatedly!"); } } finally { this.lock.unlock(); } } public T getBean(Class requiredType) { try { return this.applicationContext.getBean(requiredType); } catch (NoSuchBeanDefinitionException error) { return null; // ignore } } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } public Environment getEnvironment() { return environment; } public void setEnvironment(Environment environment) { this.environment = environment; } public void setBeanFactory(TransactionBeanFactory tbf) { this.beanFactory = tbf; } public TransactionBeanFactory getBeanFactory() { return beanFactory; } } ================================================ FILE: bytejta-supports-dubbo/src/main/java/org/bytesoft/bytejta/supports/dubbo/config/DubboSupportConfiguration.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.bytejta.supports.dubbo.config; import javax.transaction.UserTransaction; import org.bytesoft.bytejta.supports.resource.properties.ConnectorResourcePropertySourceFactory; import org.bytesoft.transaction.TransactionManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.EnvironmentAware; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.context.annotation.ImportResource; import org.springframework.context.annotation.PropertySource; import org.springframework.core.env.Environment; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.annotation.TransactionManagementConfigurer; import org.springframework.transaction.jta.JtaTransactionManager; @PropertySource(value = "bytejta:connector.config", factory = ConnectorResourcePropertySourceFactory.class) @ImportResource({ "classpath:bytejta-supports-dubbo.xml" }) @EnableAspectJAutoProxy(proxyTargetClass = true) @EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class }) @EnableTransactionManagement public class DubboSupportConfiguration implements TransactionManagementConfigurer, ApplicationContextAware, EnvironmentAware { static final Logger logger = LoggerFactory.getLogger(DubboSupportConfiguration.class); private Environment environment; private ApplicationContext applicationContext; public PlatformTransactionManager annotationDrivenTransactionManager() { JtaTransactionManager jtaTransactionManager = new JtaTransactionManager(); jtaTransactionManager.setTransactionManager(this.applicationContext.getBean(TransactionManager.class)); jtaTransactionManager.setUserTransaction(this.applicationContext.getBean(UserTransaction.class)); return jtaTransactionManager; } public Environment getEnvironment() { return environment; } public void setEnvironment(Environment environment) { this.environment = environment; } public ApplicationContext getApplicationContext() { return applicationContext; } public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } } ================================================ FILE: bytejta-supports-dubbo/src/main/java/org/bytesoft/bytejta/supports/dubbo/ext/ILoadBalancer.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.bytejta.supports.dubbo.ext; import java.util.List; import com.alibaba.dubbo.common.URL; import com.alibaba.dubbo.common.extension.SPI; import com.alibaba.dubbo.rpc.Invocation; import com.alibaba.dubbo.rpc.Invoker; import com.alibaba.dubbo.rpc.RpcException; @SPI public interface ILoadBalancer { public Invoker select(List> invokers, URL url, Invocation invocation) throws RpcException; } ================================================ FILE: bytejta-supports-dubbo/src/main/java/org/bytesoft/bytejta/supports/dubbo/internal/TransactionBeanConfigValidator.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.dubbo.internal; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.FatalBeanException; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.PropertyValue; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import com.alibaba.dubbo.config.ProtocolConfig; import com.alibaba.dubbo.config.spring.ServiceBean; import com.alibaba.dubbo.remoting.RemotingException; public class TransactionBeanConfigValidator implements BeanPostProcessor, BeanFactoryPostProcessor { static final Logger logger = LoggerFactory.getLogger(TransactionBeanConfigValidator.class); public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (ProtocolConfig.class.isInstance(bean)) { this.validateProtocolConfig(beanName, (ProtocolConfig) bean); } else if (ServiceBean.class.isInstance(bean)) { this.validateServiceBean(beanName, (ServiceBean) bean); } return bean; } private void validateProtocolConfig(String beanName, ProtocolConfig protocolConfig) throws BeansException { Integer port = protocolConfig.getPort(); if (port == null) { throw new FatalBeanException( "The value of the attribute 'port' () must be explicitly specified."); } else if (port.intValue() <= 0) { throw new FatalBeanException( "The value of the attribute 'port' () must be explicitly specified and not equal to -1."); } } private void validateServiceBean(String beanName, ServiceBean serviceBean) throws BeansException { Integer retries = serviceBean.getRetries(); String loadbalance = serviceBean.getLoadbalance(); String cluster = serviceBean.getCluster(); String filter = serviceBean.getFilter(); String group = serviceBean.getGroup(); if (StringUtils.isBlank(group)) { return; } else if (StringUtils.equalsIgnoreCase("x-bytejta", group) == false && StringUtils.lowerCase(group).startsWith("x-bytejta-") == false) { return; } String[] filterArray = filter == null ? new String[0] : filter.split("\\s*,\\s*"); if (retries != null && retries.intValue() >= 0) { throw new FatalBeanException(String.format("The value of attr 'retries'(beanId= %s) should be '-1'.", beanName)); } else if (loadbalance == null || StringUtils.equals("bytejta", loadbalance) == false) { throw new FatalBeanException( String.format("The value of attr 'loadbalance'(beanId= %s) should be 'bytejta'.", beanName)); } else if (cluster == null || StringUtils.equals("failfast", cluster) == false) { throw new FatalBeanException( String.format("The value of attribute 'cluster' (beanId= %s) must be 'failfast'.", beanName)); } else if (filterArray.length == 0) { throw new FatalBeanException(String.format("The value of attr 'filter'(beanId= %s) must be null.", beanName)); } else if (StringUtils.equalsIgnoreCase(filterArray[0], "bytejta") == false) { throw new FatalBeanException( String.format("The first value of attr 'filter'(beanId= %s) should be 'bytejta'.", beanName)); } } private void validateReferenceConfig(String beanName, BeanDefinition beanDef) throws BeansException { MutablePropertyValues mpv = beanDef.getPropertyValues(); PropertyValue group = mpv.getPropertyValue("group"); PropertyValue retries = mpv.getPropertyValue("retries"); PropertyValue loadbalance = mpv.getPropertyValue("loadbalance"); PropertyValue cluster = mpv.getPropertyValue("cluster"); PropertyValue filter = mpv.getPropertyValue("filter"); String filterValue = filter == null || filter.getValue() == null ? null : String.valueOf(filter.getValue()); String[] filterArray = filter == null ? new String[0] : filterValue.split("\\s*,\\s*"); if (group == null || group.getValue() == null // || ("x-bytejta".equals(group.getValue()) || String.valueOf(group.getValue()).startsWith("x-bytejta-")) == false) { throw new FatalBeanException(String.format( "The value of attr 'group'(beanId= %s) should be 'x-bytejta' or starts with 'x-bytejta-'.", beanName)); } else if (retries == null || retries.getValue() == null || "-1".equals(retries.getValue()) == false) { throw new FatalBeanException(String.format("The value of attr 'retries'(beanId= %s) should be '-1'.", beanName)); } else if (loadbalance == null || loadbalance.getValue() == null || "bytejta".equals(loadbalance.getValue()) == false) { throw new FatalBeanException( String.format("The value of attr 'loadbalance'(beanId= %s) should be 'bytejta'.", beanName)); } else if (cluster == null || cluster.getValue() == null || "failfast".equals(cluster.getValue()) == false) { throw new FatalBeanException( String.format("The value of attribute 'cluster' (beanId= %s) must be 'failfast'.", beanName)); } else if (filter == null || filter.getValue() == null || String.class.isInstance(filter.getValue()) == false) { throw new FatalBeanException(String .format("The value of attr 'filter'(beanId= %s) must be java.lang.String and cannot be null.", beanName)); } else if (StringUtils.equalsIgnoreCase(filterArray[filterArray.length - 1], "bytejta") == false) { throw new FatalBeanException( String.format("The last value of attr 'filter'(beanId= %s) should be 'bytejta'.", beanName)); } PropertyValue pv = mpv.getPropertyValue("interface"); String clazzName = String.valueOf(pv.getValue()); ClassLoader cl = Thread.currentThread().getContextClassLoader(); Class clazz = null; try { clazz = cl.loadClass(clazzName); } catch (Exception ex) { throw new FatalBeanException(String.format("Cannot load class %s.", clazzName)); } Method[] methodArray = clazz.getMethods(); for (int i = 0; i < methodArray.length; i++) { Method method = methodArray[i]; boolean declared = false; Class[] exceptionTypeArray = method.getExceptionTypes(); for (int j = 0; j < exceptionTypeArray.length; j++) { Class exceptionType = exceptionTypeArray[j]; if (RemotingException.class.isAssignableFrom(exceptionType)) { declared = true; break; } } if (declared == false) { logger.warn("The remote call method({}) should be declared to throw a remote exception: {}!", method, RemotingException.class.getName()); } } } public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { ClassLoader cl = Thread.currentThread().getContextClassLoader(); Map referenceMap = new HashMap(); Map> clazzMap = new HashMap>(); String[] beanNameArray = beanFactory.getBeanDefinitionNames(); for (int i = 0; i < beanNameArray.length; i++) { String beanName = beanNameArray[i]; BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName); String beanClassName = beanDef.getBeanClassName(); if (StringUtils.equals(com.alibaba.dubbo.config.spring.ReferenceBean.class.getName(), beanClassName) == false) { continue; } Class beanClass = null; try { beanClass = cl.loadClass(beanClassName); } catch (Exception ex) { logger.debug("Cannot load class {}, beanId= {}!", beanClassName, beanName, ex); continue; } clazzMap.put(beanClassName, beanClass); } for (int i = 0; i < beanNameArray.length; i++) { String beanName = beanNameArray[i]; BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName); String beanClassName = beanDef.getBeanClassName(); Class beanClass = clazzMap.get(beanClassName); if (com.alibaba.dubbo.config.spring.ReferenceBean.class.equals(beanClass)) { MutablePropertyValues mpv = beanDef.getPropertyValues(); PropertyValue group = mpv.getPropertyValue("group"); if (group == null || group.getValue() == null) { continue; } String groupValue = String.valueOf(group.getValue()); if (StringUtils.equalsIgnoreCase("x-bytejta", groupValue)) { referenceMap.put(beanName, beanDef); } else if (StringUtils.lowerCase(groupValue).startsWith("x-bytejta-")) { referenceMap.put(beanName, beanDef); } } } for (Iterator> itr = referenceMap.entrySet().iterator(); itr.hasNext();) { Map.Entry entry = itr.next(); this.validateReferenceConfig(entry.getKey(), entry.getValue()); } } } ================================================ FILE: bytejta-supports-dubbo/src/main/java/org/bytesoft/bytejta/supports/dubbo/internal/TransactionEndpointAutoInjector.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.dubbo.internal; import java.util.Iterator; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.bytesoft.common.utils.CommonUtils; import org.bytesoft.transaction.aware.TransactionEndpointAware; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.FatalBeanException; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import com.alibaba.dubbo.common.utils.ConfigUtils; public class TransactionEndpointAutoInjector implements InitializingBean, BeanPostProcessor, ApplicationContextAware { static final Logger logger = LoggerFactory.getLogger(TransactionEndpointAutoInjector.class); private ApplicationContext applicationContext; public void afterPropertiesSet() throws Exception { String host = CommonUtils.getInetAddress(); String name = null; try { com.alibaba.dubbo.config.ApplicationConfig applicationConfig = // this.applicationContext.getBean(com.alibaba.dubbo.config.ApplicationConfig.class); name = String.valueOf(applicationConfig.getName()); } catch (NoSuchBeanDefinitionException error) { String application = ConfigUtils.getProperty("dubbo.application.name"); if (StringUtils.isBlank(application)) { throw new FatalBeanException("No configuration of class com.alibaba.dubbo.config.ApplicationConfig was found."); } name = application; } String port = null; try { com.alibaba.dubbo.config.ProtocolConfig protocolConfig = // this.applicationContext.getBean(com.alibaba.dubbo.config.ProtocolConfig.class); if (protocolConfig.getPort() == null) { throw new IllegalStateException(); } port = String.valueOf(protocolConfig.getPort()); } catch (NoSuchBeanDefinitionException error) { String serverPort = ConfigUtils.getProperty("dubbo.protocol.dubbo.port"); if (StringUtils.isBlank(serverPort)) { throw new FatalBeanException("No configuration of class com.alibaba.dubbo.config.ProtocolConfig was found."); } port = serverPort; } catch (IllegalStateException error) { String serverPort = ConfigUtils.getProperty("dubbo.protocol.dubbo.port"); if (StringUtils.isBlank(serverPort)) { throw new FatalBeanException("No configuration of class com.alibaba.dubbo.config.ProtocolConfig was found."); } port = serverPort; } if (StringUtils.isBlank(name)) { throw new FatalBeanException("No configuration of class com.alibaba.dubbo.config.ApplicationConfig was found."); } if (StringUtils.isBlank(port)) { throw new FatalBeanException( "The value of the attribute 'port' () must be explicitly specified."); } else if (Integer.valueOf(port) <= 0) { throw new FatalBeanException( "The value of the attribute 'port' () can not equal to -1."); } String identifier = String.format("%s:%s:%s", host, name, port); Map beanMap = // this.applicationContext.getBeansOfType(TransactionEndpointAware.class); for (Iterator> itr = beanMap.entrySet().iterator(); itr.hasNext();) { Map.Entry entry = itr.next(); TransactionEndpointAware aware = entry.getValue(); aware.setEndpoint(identifier); } // end-for (Iterator> itr = beanMap.entrySet().iterator(); itr.hasNext();) } public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (TransactionEndpointAware.class.isInstance(bean)) { TransactionEndpointAware aware = (TransactionEndpointAware) bean; this.initializeEndpointIfNecessary(aware); } // end-if (TransactionEndpointAware.class.isInstance(bean)) return bean; } public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (TransactionEndpointAware.class.isInstance(bean)) { TransactionEndpointAware aware = (TransactionEndpointAware) bean; this.initializeEndpointIfNecessary(aware); } // end-if (TransactionEndpointAware.class.isInstance(bean)) return bean; } private void initializeEndpointIfNecessary(TransactionEndpointAware aware) { if (StringUtils.isBlank(aware.getEndpoint())) { String host = CommonUtils.getInetAddress(); String name = null; try { com.alibaba.dubbo.config.ApplicationConfig applicationConfig = // this.applicationContext.getBean(com.alibaba.dubbo.config.ApplicationConfig.class); name = String.valueOf(applicationConfig.getName()); } catch (NoSuchBeanDefinitionException error) { String application = ConfigUtils.getProperty("dubbo.application.name"); if (StringUtils.isBlank(application)) { throw error; } else { name = application; } } String port = null; try { com.alibaba.dubbo.config.ProtocolConfig protocolConfig = // this.applicationContext.getBean(com.alibaba.dubbo.config.ProtocolConfig.class); port = String.valueOf(protocolConfig.getPort()); } catch (NoSuchBeanDefinitionException error) { String serverPort = ConfigUtils.getProperty("dubbo.protocol.dubbo.port"); if (StringUtils.isBlank(serverPort)) { throw error; } else { port = serverPort; } } aware.setEndpoint(String.format("%s:%s:%s", host, name, port)); } } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } } ================================================ FILE: bytejta-supports-dubbo/src/main/java/org/bytesoft/bytejta/supports/dubbo/internal/TransactionParticipantRegistrant.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.bytejta.supports.dubbo.internal; import org.bytesoft.bytejta.TransactionCoordinator; import org.bytesoft.bytejta.supports.dubbo.TransactionBeanRegistry; import org.bytesoft.common.utils.CommonUtils; import org.bytesoft.transaction.aware.TransactionEndpointAware; import org.bytesoft.transaction.remote.RemoteCoordinator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.FatalBeanException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.beans.factory.config.SingletonBeanRegistry; import com.alibaba.dubbo.common.Constants; import com.alibaba.dubbo.config.ReferenceConfig; import com.alibaba.dubbo.config.ServiceConfig; public class TransactionParticipantRegistrant implements SmartInitializingSingleton, TransactionEndpointAware, BeanFactoryAware { static final Logger logger = LoggerFactory.getLogger(TransactionParticipantRegistrant.class); private BeanFactory beanFactory; private String endpoint; public void afterSingletonsInstantiated() { TransactionCoordinator transactionCoordinator = this.beanFactory.getBean(TransactionCoordinator.class); TransactionBeanRegistry beanRegistry = this.beanFactory.getBean(TransactionBeanRegistry.class); if (transactionCoordinator == null) { throw new FatalBeanException("No configuration of class org.bytesoft.bytejta.TransactionCoordinator was found."); } else if (beanRegistry == null) { throw new FatalBeanException( "No configuration of class org.bytesoft.bytejta.supports.dubbo.TransactionBeanRegistry was found."); } this.initializeForProvider(transactionCoordinator); this.initializeForConsumer(beanRegistry); } public void initializeForProvider(RemoteCoordinator reference) throws BeansException { SingletonBeanRegistry registry = (SingletonBeanRegistry) this.beanFactory; ServiceConfig globalServiceConfig = new ServiceConfig(); globalServiceConfig.setInterface(RemoteCoordinator.class); globalServiceConfig.setRef(reference); globalServiceConfig.setCluster("failfast"); globalServiceConfig.setLoadbalance("bytejta"); globalServiceConfig.setFilter("bytejta"); globalServiceConfig.setGroup("org-bytesoft-bytejta"); globalServiceConfig.setRetries(-1); globalServiceConfig.setTimeout(15000); ServiceConfig applicationServiceConfig = new ServiceConfig(); applicationServiceConfig.setInterface(RemoteCoordinator.class); applicationServiceConfig.setRef(reference); applicationServiceConfig.setCluster("failfast"); applicationServiceConfig.setLoadbalance("bytejta"); applicationServiceConfig.setFilter("bytejta"); applicationServiceConfig.setGroup(CommonUtils.getApplication(this.endpoint)); applicationServiceConfig.setRetries(-1); applicationServiceConfig.setTimeout(15000); try { com.alibaba.dubbo.config.ApplicationConfig applicationConfig = // this.beanFactory.getBean(com.alibaba.dubbo.config.ApplicationConfig.class); globalServiceConfig.setApplication(applicationConfig); applicationServiceConfig.setApplication(applicationConfig); } catch (NoSuchBeanDefinitionException error) { logger.warn("No configuration of class com.alibaba.dubbo.config.ApplicationConfig was found."); } try { com.alibaba.dubbo.config.RegistryConfig registryConfig = // this.beanFactory.getBean(com.alibaba.dubbo.config.RegistryConfig.class); if (registryConfig != null) { globalServiceConfig.setRegistry(registryConfig); applicationServiceConfig.setRegistry(registryConfig); } } catch (NoSuchBeanDefinitionException error) { logger.warn("No configuration of class com.alibaba.dubbo.config.RegistryConfig was found."); } try { com.alibaba.dubbo.config.ProtocolConfig protocolConfig = // this.beanFactory.getBean(com.alibaba.dubbo.config.ProtocolConfig.class); globalServiceConfig.setProtocol(protocolConfig); applicationServiceConfig.setProtocol(protocolConfig); } catch (NoSuchBeanDefinitionException error) { logger.warn("No configuration of class com.alibaba.dubbo.config.ProtocolConfig was found."); } globalServiceConfig.export(); applicationServiceConfig.export(); String globalSkeletonBeanId = String.format("skeleton@%s", RemoteCoordinator.class.getName()); registry.registerSingleton(globalSkeletonBeanId, globalServiceConfig); String applicationSkeletonBeanId = // String.format("%s@%s", CommonUtils.getApplication(this.endpoint), RemoteCoordinator.class.getName()); registry.registerSingleton(applicationSkeletonBeanId, applicationServiceConfig); } public void initializeForConsumer(TransactionBeanRegistry beanRegistry) throws BeansException { SingletonBeanRegistry registry = (SingletonBeanRegistry) this.beanFactory; ReferenceConfig referenceConfig = new ReferenceConfig(); referenceConfig.setInterface(RemoteCoordinator.class); referenceConfig.setTimeout(15000); referenceConfig.setCluster("failfast"); referenceConfig.setLoadbalance("bytejta"); referenceConfig.setFilter("bytejta"); referenceConfig.setGroup("org-bytesoft-bytejta"); referenceConfig.setCheck(false); referenceConfig.setRetries(-1); referenceConfig.setScope(Constants.SCOPE_REMOTE); try { com.alibaba.dubbo.config.ApplicationConfig applicationConfig = // this.beanFactory.getBean(com.alibaba.dubbo.config.ApplicationConfig.class); referenceConfig.setApplication(applicationConfig); } catch (NoSuchBeanDefinitionException error) { logger.warn("No configuration of class com.alibaba.dubbo.config.ApplicationConfig was found."); } try { com.alibaba.dubbo.config.RegistryConfig registryConfig = // this.beanFactory.getBean(com.alibaba.dubbo.config.RegistryConfig.class); if (registryConfig != null) { referenceConfig.setRegistry(registryConfig); } } catch (NoSuchBeanDefinitionException error) { logger.warn("No configuration of class com.alibaba.dubbo.config.RegistryConfig was found."); } try { com.alibaba.dubbo.config.ProtocolConfig protocolConfig = // this.beanFactory.getBean(com.alibaba.dubbo.config.ProtocolConfig.class); referenceConfig.setProtocol(protocolConfig.getName()); } catch (NoSuchBeanDefinitionException error) { logger.warn("No configuration of class com.alibaba.dubbo.config.ProtocolConfig was found."); } RemoteCoordinator globalCoordinator = referenceConfig.get(); beanRegistry.setConsumeCoordinator(globalCoordinator); String stubBeanId = String.format("stub@%s", RemoteCoordinator.class.getName()); registry.registerSingleton(stubBeanId, globalCoordinator); } public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; } public String getEndpoint() { return this.endpoint; } public void setEndpoint(String identifier) { this.endpoint = identifier; } } ================================================ FILE: bytejta-supports-dubbo/src/main/java/org/bytesoft/bytejta/supports/dubbo/serialize/XAResourceDeserializerImpl.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.dubbo.serialize; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; import org.bytesoft.bytejta.supports.dubbo.TransactionBeanRegistry; import org.bytesoft.bytejta.supports.internal.RemoteCoordinatorRegistry; import org.bytesoft.bytejta.supports.resource.RemoteResourceDescriptor; 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.supports.resource.XAResourceDescriptor; import org.bytesoft.transaction.supports.serialize.XAResourceDeserializer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import com.alibaba.dubbo.common.Constants; import com.alibaba.dubbo.config.ApplicationConfig; import com.alibaba.dubbo.config.ProtocolConfig; import com.alibaba.dubbo.config.ReferenceConfig; import com.alibaba.dubbo.config.RegistryConfig; import com.alibaba.dubbo.rpc.RpcException; public class XAResourceDeserializerImpl implements XAResourceDeserializer, ApplicationContextAware { static final Logger logger = LoggerFactory.getLogger(XAResourceDeserializerImpl.class); static Pattern pattern = Pattern.compile("^[^:]+\\s*:\\s*[^:]+\\s*:\\s*\\d+$"); private XAResourceDeserializer resourceDeserializer; private ApplicationContext applicationContext; public XAResourceDescriptor deserialize(String identifier) { XAResourceDescriptor resourceDescriptor = this.resourceDeserializer.deserialize(identifier); if (resourceDescriptor != null) { return resourceDescriptor; } Matcher matcher = pattern.matcher(identifier); if (matcher.find()) { RemoteCoordinatorRegistry registry = RemoteCoordinatorRegistry.getInstance(); RemoteAddr remoteAddr = CommonUtils.getRemoteAddr(identifier); RemoteNode remoteNode = CommonUtils.getRemoteNode(identifier); RemoteNode targetNode = registry.getRemoteNode(remoteAddr); if (targetNode == null && remoteAddr != null && remoteNode != null) { registry.putRemoteNode(remoteAddr, remoteNode); } this.initializePhysicalInstanceIfNecessary(remoteAddr); String application = CommonUtils.getApplication(identifier); this.initializeRemoteParticipantIfNecessary(application); RemoteResourceDescriptor descriptor = new RemoteResourceDescriptor(); descriptor.setIdentifier(identifier); descriptor.setDelegate(registry.getPhysicalInstance(remoteAddr)); // descriptor.setDelegate(registry.getParticipant(application)); return descriptor; } else { logger.error("can not find a matching xa-resource(identifier= {})!", identifier); return null; } } private void initializePhysicalInstanceIfNecessary(RemoteAddr remoteAddr) throws RpcException { RemoteCoordinatorRegistry participantRegistry = RemoteCoordinatorRegistry.getInstance(); RemoteCoordinator physicalInst = participantRegistry.getPhysicalInstance(remoteAddr); if (physicalInst == null) { String serverHost = remoteAddr.getServerHost(); int serverPort = remoteAddr.getServerPort(); final String target = String.format("%s:%s", serverHost, serverPort).intern(); synchronized (target) { RemoteCoordinator participant = participantRegistry.getPhysicalInstance(remoteAddr); if (participant == null) { this.processInitPhysicalInstanceIfNecessary(remoteAddr); } } // end-synchronized (target) } // end-if (physicalInst == null) } private void processInitPhysicalInstanceIfNecessary(RemoteAddr remoteAddr) throws RpcException { RemoteCoordinatorRegistry participantRegistry = RemoteCoordinatorRegistry.getInstance(); TransactionBeanRegistry beanRegistry = TransactionBeanRegistry.getInstance(); RemoteCoordinator participant = participantRegistry.getPhysicalInstance(remoteAddr); if (participant == null) { ApplicationConfig applicationConfig = beanRegistry.getBean(ApplicationConfig.class); RegistryConfig registryConfig = beanRegistry.getBean(RegistryConfig.class); ProtocolConfig protocolConfig = beanRegistry.getBean(ProtocolConfig.class); ReferenceConfig referenceConfig = new ReferenceConfig(); referenceConfig.setInterface(RemoteCoordinator.class); referenceConfig.setTimeout(15000); referenceConfig.setCluster("failfast"); referenceConfig.setLoadbalance("bytejta"); referenceConfig.setFilter("bytejta"); referenceConfig.setGroup("org-bytesoft-bytejta"); referenceConfig.setCheck(false); referenceConfig.setRetries(-1); referenceConfig.setUrl(String.format("%s:%s", remoteAddr.getServerHost(), remoteAddr.getServerPort())); referenceConfig.setScope(Constants.SCOPE_REMOTE); referenceConfig.setApplication(applicationConfig); if (registryConfig != null) { referenceConfig.setRegistry(registryConfig); } if (protocolConfig != null) { referenceConfig.setProtocol(protocolConfig.getName()); } // end-if (protocolConfig != null) RemoteCoordinator reference = referenceConfig.get(); if (reference == null) { throw new RpcException("Cannot get the application name of the remote application."); } participantRegistry.putPhysicalInstance(remoteAddr, reference); } } private void initializeRemoteParticipantIfNecessary(final String system) throws RpcException { RemoteCoordinatorRegistry participantRegistry = RemoteCoordinatorRegistry.getInstance(); final String application = StringUtils.trimToEmpty(system).intern(); RemoteCoordinator remoteParticipant = participantRegistry.getParticipant(application); if (remoteParticipant == null) { synchronized (application) { RemoteCoordinator participant = participantRegistry.getParticipant(application); if (participant == null) { this.processInitRemoteParticipantIfNecessary(application); } } // end-synchronized (target) } // end-if (remoteParticipant == null) } private void processInitRemoteParticipantIfNecessary(String application) { RemoteCoordinatorRegistry participantRegistry = RemoteCoordinatorRegistry.getInstance(); TransactionBeanRegistry beanRegistry = TransactionBeanRegistry.getInstance(); RemoteCoordinator participant = participantRegistry.getParticipant(application); if (participant == null) { ApplicationConfig applicationConfig = beanRegistry.getBean(ApplicationConfig.class); RegistryConfig registryConfig = beanRegistry.getBean(RegistryConfig.class); ProtocolConfig protocolConfig = beanRegistry.getBean(ProtocolConfig.class); ReferenceConfig referenceConfig = new ReferenceConfig(); referenceConfig.setInterface(RemoteCoordinator.class); referenceConfig.setTimeout(15000); referenceConfig.setCluster("failfast"); referenceConfig.setLoadbalance("bytejta"); referenceConfig.setFilter("bytejta"); referenceConfig.setGroup(application); referenceConfig.setCheck(false); referenceConfig.setRetries(-1); referenceConfig.setScope(Constants.SCOPE_REMOTE); referenceConfig.setApplication(applicationConfig); if (registryConfig != null) { referenceConfig.setRegistry(registryConfig); } if (protocolConfig != null) { referenceConfig.setProtocol(protocolConfig.getName()); } // end-if (protocolConfig != null) RemoteCoordinator reference = referenceConfig.get(); if (reference == null) { throw new RpcException("Cannot get the application name of the remote application."); } participantRegistry.putParticipant(application, reference); } } public XAResourceDeserializer getResourceDeserializer() { return resourceDeserializer; } public void setResourceDeserializer(XAResourceDeserializer resourceDeserializer) { this.resourceDeserializer = resourceDeserializer; } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } public ApplicationContext getApplicationContext() { return applicationContext; } } ================================================ FILE: bytejta-supports-dubbo/src/main/java/org/bytesoft/bytejta/supports/dubbo/spi/TransactionLoadBalance.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.dubbo.spi; import java.util.List; import org.bytesoft.bytejta.TransactionImpl; import org.bytesoft.bytejta.supports.dubbo.InvocationContextRegistry; import org.bytesoft.bytejta.supports.dubbo.TransactionBeanRegistry; import org.bytesoft.bytejta.supports.dubbo.ext.ILoadBalancer; import org.bytesoft.bytejta.supports.internal.RemoteCoordinatorRegistry; import org.bytesoft.common.utils.CommonUtils; import org.bytesoft.transaction.TransactionBeanFactory; import org.bytesoft.transaction.TransactionManager; import org.bytesoft.transaction.archive.XAResourceArchive; import org.bytesoft.transaction.remote.RemoteAddr; import org.bytesoft.transaction.remote.RemoteNode; import org.bytesoft.transaction.supports.resource.XAResourceDescriptor; import org.springframework.core.env.Environment; import com.alibaba.dubbo.common.URL; import com.alibaba.dubbo.common.extension.ExtensionLoader; import com.alibaba.dubbo.rpc.Invocation; import com.alibaba.dubbo.rpc.Invoker; import com.alibaba.dubbo.rpc.RpcException; import com.alibaba.dubbo.rpc.cluster.LoadBalance; public final class TransactionLoadBalance implements LoadBalance { static final String CONSTANT_LOADBALANCE_KEY = "org.bytesoft.bytejta.loadbalance"; private ILoadBalancer loadBalancer; private void fireInitializeIfNecessary() { if (this.loadBalancer == null) { this.initializeIfNecessary(); } } private synchronized void initializeIfNecessary() { if (this.loadBalancer == null) { Environment environment = TransactionBeanRegistry.getInstance().getEnvironment(); String loadBalanceKey = environment.getProperty(CONSTANT_LOADBALANCE_KEY, "default"); ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(ILoadBalancer.class); this.loadBalancer = extensionLoader.getExtension(loadBalanceKey); } } public Invoker select(List> invokers, URL url, Invocation invocation) throws RpcException { InvocationContextRegistry registry = InvocationContextRegistry.getInstance(); RemoteNode invocationContext = registry.getInvocationContext(); if (invocationContext == null) { return this.selectConfigedInvoker(invokers, url, invocation); } else { return this.selectSpecificInvoker(invokers, url, invocation, invocationContext); } } public Invoker selectConfigedInvoker(List> invokers, URL url, Invocation invocation) throws RpcException { if (invokers == null || invokers.isEmpty()) { throw new RpcException("No invoker is found!"); } TransactionBeanFactory beanFactory = TransactionBeanRegistry.getInstance().getBeanFactory(); RemoteCoordinatorRegistry participantRegistry = RemoteCoordinatorRegistry.getInstance(); TransactionManager transactionManager = beanFactory.getTransactionManager(); TransactionImpl transaction = // (TransactionImpl) transactionManager.getTransactionQuietly(); List participantList = transaction == null ? null : transaction.getRemoteParticipantList(); RemoteAddr instanceAddr = null; for (int i = 0; invokers != null && participantList != null && participantList.isEmpty() == false && i < invokers.size(); i++) { Invoker invoker = invokers.get(i); URL invokerUrl = invoker.getUrl(); RemoteAddr invokerAddr = new RemoteAddr(); invokerAddr.setServerHost(invokerUrl.getHost()); invokerAddr.setServerPort(invokerUrl.getPort()); RemoteNode remoteNode = participantRegistry.getRemoteNode(invokerAddr); if (remoteNode == null) { continue; } XAResourceDescriptor participant = transaction.getRemoteCoordinator(remoteNode.getServiceKey()); if (participant == null) { continue; } String identifier = participant.getIdentifier(); RemoteAddr remoteAddr = CommonUtils.getRemoteAddr(identifier); if (invokerAddr.equals(remoteAddr) == false) { instanceAddr = remoteAddr; continue; } return invoker; } if (instanceAddr != null) { throw new RpcException( String.format("Invoker(%s:%s) is not found!", instanceAddr.getServerHost(), instanceAddr.getServerPort())); } this.fireInitializeIfNecessary(); if (this.loadBalancer == null) { throw new RpcException("No org.bytesoft.bytejta.supports.dubbo.ext.ILoadBalancer is found!"); } else { return this.loadBalancer.select(invokers, url, invocation); } } public Invoker selectSpecificInvoker(List> invokers, URL url, Invocation invocation, RemoteNode context) throws RpcException { RemoteAddr remoteAddr = new RemoteAddr(); remoteAddr.setServerHost(context.getServerHost()); remoteAddr.setServerPort(context.getServerPort()); for (int i = 0; invokers != null && i < invokers.size(); i++) { Invoker invoker = invokers.get(i); URL targetUrl = invoker.getUrl(); RemoteAddr targetAddr = new RemoteAddr(); targetAddr.setServerHost(targetUrl.getIp()); targetAddr.setServerPort(targetUrl.getPort()); if (targetAddr.equals(remoteAddr)) { return invoker; } // end-if (targetAddr.equals(remoteAddr)) } throw new RpcException(String.format("Invoker(%s:%s) is not found!", context.getServerHost(), context.getServerPort())); } } ================================================ FILE: bytejta-supports-dubbo/src/main/java/org/bytesoft/bytejta/supports/dubbo/spi/TransactionLoadBalancer.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.bytejta.supports.dubbo.spi; import java.util.List; import java.util.Random; import org.bytesoft.bytejta.supports.dubbo.ext.ILoadBalancer; import com.alibaba.dubbo.common.URL; import com.alibaba.dubbo.rpc.Invocation; import com.alibaba.dubbo.rpc.Invoker; import com.alibaba.dubbo.rpc.RpcException; public class TransactionLoadBalancer implements ILoadBalancer { static final Random random = new Random(); public Invoker select(List> invokers, URL url, Invocation invocation) throws RpcException { if (invokers == null || invokers.isEmpty()) { throw new RpcException("No invoker is found!"); } int lengthOfInvokerList = invokers == null ? 0 : invokers.size(); return invokers.get(random.nextInt(lengthOfInvokerList)); } } ================================================ FILE: bytejta-supports-dubbo/src/main/java/org/bytesoft/bytejta/supports/dubbo/spi/TransactionServiceFilter.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.dubbo.spi; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.Serializable; import java.lang.reflect.Proxy; import java.util.HashMap; import java.util.Map; 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.dubbo.DubboRemoteCoordinator; import org.bytesoft.bytejta.supports.dubbo.TransactionBeanRegistry; import org.bytesoft.bytejta.supports.internal.RemoteCoordinatorRegistry; import org.bytesoft.bytejta.supports.internal.RemoteCoordinatorRegistry.InvocationDef; import org.bytesoft.bytejta.supports.rpc.TransactionRequestImpl; import org.bytesoft.bytejta.supports.rpc.TransactionResponseImpl; import org.bytesoft.common.utils.ByteUtils; import org.bytesoft.common.utils.CommonUtils; 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.TransactionParticipant; import org.bytesoft.transaction.TransactionRepository; import org.bytesoft.transaction.remote.RemoteAddr; import org.bytesoft.transaction.remote.RemoteCoordinator; import org.bytesoft.transaction.remote.RemoteNode; import org.bytesoft.transaction.supports.rpc.TransactionInterceptor; import org.bytesoft.transaction.xa.TransactionXid; import org.bytesoft.transaction.xa.XidFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.transaction.annotation.Propagation; import com.alibaba.com.caucho.hessian.io.HessianHandle; import com.alibaba.dubbo.common.Constants; import com.alibaba.dubbo.common.URL; import com.alibaba.dubbo.config.ApplicationConfig; import com.alibaba.dubbo.config.ProtocolConfig; import com.alibaba.dubbo.config.ReferenceConfig; import com.alibaba.dubbo.config.RegistryConfig; import com.alibaba.dubbo.rpc.Filter; import com.alibaba.dubbo.rpc.Invocation; import com.alibaba.dubbo.rpc.Invoker; import com.alibaba.dubbo.rpc.Result; import com.alibaba.dubbo.rpc.RpcContext; import com.alibaba.dubbo.rpc.RpcException; import com.alibaba.dubbo.rpc.RpcResult; import com.caucho.hessian.io.HessianInput; import com.caucho.hessian.io.HessianOutput; public class TransactionServiceFilter implements Filter { static final String KEY_XA_RESOURCE_START = "start"; static final String KEY_XA_GET_IDENTIFIER = "getIdentifier"; static final String KEY_XA_GET_APPLICATION = "getApplication"; static final String KEY_XA_GET_REMOTEADDR = "getRemoteAddr"; static final String KEY_XA_GET_REMOTENODE = "getRemoteNode"; static final Logger logger = LoggerFactory.getLogger(TransactionServiceFilter.class); public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { if (RpcContext.getContext().isProviderSide()) { return this.providerInvoke(invoker, invocation); } else { return this.consumerInvoke(invoker, invocation); } } public Result providerInvoke(Invoker invoker, Invocation invocation) throws RpcException { String interfaceClazz = RpcContext.getContext().getUrl().getServiceInterface(); boolean participantFlag = TransactionParticipant.class.getName().equals(interfaceClazz); boolean xaResourceFlag = XAResource.class.getName().equals(interfaceClazz); boolean coordinatorFlag = RemoteCoordinator.class.getName().equals(interfaceClazz); if (participantFlag == false && xaResourceFlag == false && coordinatorFlag == false) { return this.providerInvokeForSVC(invoker, invocation); } else if (StringUtils.equals(invocation.getMethodName(), KEY_XA_RESOURCE_START)) { return this.providerInvokeForKey(invoker, invocation); } else if (StringUtils.equals(invocation.getMethodName(), KEY_XA_GET_IDENTIFIER)) { return this.providerInvokeForKey(invoker, invocation); } else if (StringUtils.equals(invocation.getMethodName(), KEY_XA_GET_APPLICATION)) { return this.providerInvokeForKey(invoker, invocation); } else if (StringUtils.equals(invocation.getMethodName(), KEY_XA_GET_REMOTEADDR)) { return this.providerInvokeForKey(invoker, invocation); } else if (StringUtils.equals(invocation.getMethodName(), KEY_XA_GET_REMOTENODE)) { return this.providerInvokeForKey(invoker, invocation); } else { return this.providerInvokeForJTA(invoker, invocation); } } public Result providerInvokeForKey(Invoker invoker, Invocation invocation) throws RpcException { TransactionBeanRegistry beanRegistry = TransactionBeanRegistry.getInstance(); TransactionBeanFactory beanFactory = beanRegistry.getBeanFactory(); RemoteCoordinator transactionCoordinator = (RemoteCoordinator) beanFactory.getNativeParticipant(); String instanceId = StringUtils.trimToEmpty(invocation.getAttachment(RemoteCoordinator.class.getName())); this.registerRemoteParticipantIfNecessary(instanceId); RpcResult result = new RpcResult(); InvocationResult wrapped = new InvocationResult(); wrapped.setVariable(RemoteCoordinator.class.getName(), transactionCoordinator.getIdentifier()); result.setException(null); result.setValue(wrapped); return result; } public Result providerInvokeForJTA(Invoker invoker, Invocation invocation) throws RpcException { TransactionBeanRegistry beanRegistry = TransactionBeanRegistry.getInstance(); TransactionBeanFactory beanFactory = beanRegistry.getBeanFactory(); XidFactory xidFactory = beanFactory.getXidFactory(); TransactionRepository transactionRepository = beanFactory.getTransactionRepository(); RemoteCoordinator transactionCoordinator = (RemoteCoordinator) beanFactory.getNativeParticipant(); Class[] parameterTypeArray = invocation.getParameterTypes(); Class parameterType = (parameterTypeArray == null || parameterTypeArray.length == 0) ? null : parameterTypeArray[0]; if (parameterTypeArray == null || parameterTypeArray.length == 0) { return this.wrapResultForProvider(invoker, invocation, null, false); } else if (Xid.class.equals(parameterType) == false) { return this.wrapResultForProvider(invoker, invocation, null, false); } RpcResult result = new RpcResult(); Object[] arguments = invocation.getArguments(); Xid xid = (Xid) arguments[0]; TransactionXid globalXid = xidFactory.createGlobalXid(xid.getGlobalTransactionId()); Transaction transaction = null; try { transaction = transactionRepository.getTransaction(globalXid); } catch (TransactionException tex) { InvocationResult wrapped = new InvocationResult(); wrapped.setError(new XAException(XAException.XAER_RMERR)); wrapped.setVariable(RemoteCoordinator.class.getName(), transactionCoordinator.getIdentifier()); result.setException(null); result.setValue(wrapped); return result; } if (transaction == null) { InvocationResult wrapped = new InvocationResult(); wrapped.setError(new XAException(XAException.XAER_NOTA)); wrapped.setVariable(RemoteCoordinator.class.getName(), transactionCoordinator.getIdentifier()); result.setException(null); result.setValue(wrapped); } else { TransactionContext transactionContext = transaction.getTransactionContext(); String propagatedBy = String.valueOf(transactionContext.getPropagatedBy()); String remoteAddr = invocation.getAttachment(RemoteCoordinator.class.getName()); if (StringUtils.equals(propagatedBy, remoteAddr)) { return this.wrapResultForProvider(invoker, invocation, propagatedBy, false); } InvocationResult wrapped = new InvocationResult(); wrapped.setError(new XAException(XAException.XAER_PROTO)); wrapped.setVariable(Propagation.class.getName(), String.valueOf(transactionContext.getPropagatedBy())); wrapped.setVariable(RemoteCoordinator.class.getName(), transactionCoordinator.getIdentifier()); result.setException(null); result.setValue(wrapped); logger.warn("{}| branch should be invoked by its own coordinator(expect= {}, actual= {})." // , globalXid, propagatedBy, remoteAddr); } return result; } public Result providerInvokeForSVC(Invoker invoker, Invocation invocation) throws RpcException { RemoteCoordinatorRegistry participantRegistry = RemoteCoordinatorRegistry.getInstance(); TransactionBeanRegistry beanRegistry = TransactionBeanRegistry.getInstance(); TransactionBeanFactory beanFactory = beanRegistry.getBeanFactory(); TransactionManager transactionManager = beanFactory.getTransactionManager(); String instanceId = invocation.getAttachment(RemoteCoordinator.class.getName()); RemoteAddr remoteAddr = CommonUtils.getRemoteAddr(instanceId); this.registerRemoteParticipantIfNecessary(instanceId); this.initializePhysicalInstanceIfNecessary(remoteAddr); RemoteCoordinator physical = participantRegistry.getPhysicalInstance(remoteAddr); // String application = CommonUtils.getApplication(instanceId); // RemoteCoordinator participant = // // StringUtils.isBlank(application) ? null : participantRegistry.getParticipant(application); TransactionRequestImpl request = new TransactionRequestImpl(); request.setTargetTransactionCoordinator(physical); TransactionResponseImpl response = new TransactionResponseImpl(); response.setSourceTransactionCoordinator(physical); String propagatedBy = null; boolean failure = false; try { this.beforeProviderInvokeForSVC(invocation, request, response); Transaction transaction = transactionManager.getTransactionQuietly(); TransactionContext transactionContext = transaction == null ? null : transaction.getTransactionContext(); propagatedBy = transactionContext == null ? null : String.valueOf(transactionContext.getPropagatedBy()); return this.wrapResultForProvider(invoker, invocation, propagatedBy, true); } catch (RpcException rex) { failure = true; return this.createErrorResultForProvider(rex, propagatedBy, true); } catch (Throwable rex) { failure = true; logger.error("Error occurred in remote call!", rex); return this.createErrorResultForProvider(rex, propagatedBy, true); } finally { try { this.afterProviderInvokeForSVC(invocation, request, response); } catch (RpcException rex) { if (failure) { logger.error("Error occurred in remote call!", rex); } else { return this.createErrorResultForProvider(rex, propagatedBy, true); } } catch (Throwable rex) { if (failure) { logger.error("Error occurred in remote call!", rex); } else { return this.createErrorResultForProvider(rex, propagatedBy, true); } } } } public Result wrapResultForProvider(Invoker invoker, Invocation invocation, String propagatedBy, boolean attachRequired) { try { RpcResult result = (RpcResult) invoker.invoke(invocation); if (result.hasException()) { return this.createErrorResultForProvider(result.getException(), propagatedBy, attachRequired); } else { return this.convertResultForProvider(result, propagatedBy, attachRequired); } } catch (Throwable rex) { return this.createErrorResultForProvider(rex, propagatedBy, attachRequired); } } private Result convertResultForProvider(RpcResult result, String propagatedBy, boolean attachRequired) { TransactionBeanRegistry beanRegistry = TransactionBeanRegistry.getInstance(); TransactionBeanFactory beanFactory = beanRegistry.getBeanFactory(); RemoteCoordinator transactionCoordinator = (RemoteCoordinator) beanFactory.getNativeParticipant(); Object value = result.getValue(); InvocationResult wrapped = new InvocationResult(); wrapped.setValue(value); if (attachRequired) { wrapped.setVariable(Propagation.class.getName(), propagatedBy); wrapped.setVariable(RemoteCoordinator.class.getName(), transactionCoordinator.getIdentifier()); } result.setException(null); result.setValue(wrapped); return result; } private Result createErrorResultForProvider(Throwable throwable, String propagatedBy, boolean attachRequired) { TransactionBeanRegistry beanRegistry = TransactionBeanRegistry.getInstance(); TransactionBeanFactory beanFactory = beanRegistry.getBeanFactory(); RemoteCoordinator transactionCoordinator = (RemoteCoordinator) beanFactory.getNativeParticipant(); RpcResult result = new RpcResult(); InvocationResult wrapped = new InvocationResult(); wrapped.setError(throwable); if (attachRequired) { wrapped.setVariable(Propagation.class.getName(), propagatedBy); wrapped.setVariable(RemoteCoordinator.class.getName(), transactionCoordinator.getIdentifier()); } result.setException(null); result.setValue(wrapped); return result; } private void beforeProviderInvokeForSVC(Invocation invocation, TransactionRequestImpl request, TransactionResponseImpl response) { TransactionBeanRegistry beanRegistry = TransactionBeanRegistry.getInstance(); TransactionBeanFactory beanFactory = beanRegistry.getBeanFactory(); TransactionInterceptor transactionInterceptor = beanFactory.getTransactionInterceptor(); RpcException rpcError = null; String transactionContextContent = invocation.getAttachment(TransactionContext.class.getName()); String propagatedBy = invocation.getAttachment(RemoteCoordinator.class.getName()); if (StringUtils.isNotBlank(transactionContextContent)) { byte[] requestByteArray = ByteUtils.stringToByteArray(transactionContextContent); ByteArrayInputStream bais = new ByteArrayInputStream(requestByteArray); HessianInput input = new HessianInput(bais); try { TransactionContext remoteTransactionContext = (TransactionContext) input.readObject(); remoteTransactionContext.setPropagatedBy(propagatedBy); request.setTransactionContext(remoteTransactionContext); } catch (IOException ex) { logger.error("Error occurred in remote call!", ex); rpcError = new RpcException("Error occurred in remote call!", ex); } } try { transactionInterceptor.afterReceiveRequest(request); } catch (RuntimeException rex) { logger.error("Error occurred in remote call!", rex); throw new RpcException("Error occurred in remote call!", rex); } if (rpcError != null) { throw rpcError; } } private void afterProviderInvokeForSVC(Invocation invocation, TransactionRequestImpl request, TransactionResponseImpl response) { TransactionBeanRegistry beanRegistry = TransactionBeanRegistry.getInstance(); TransactionBeanFactory beanFactory = beanRegistry.getBeanFactory(); TransactionInterceptor transactionInterceptor = beanFactory.getTransactionInterceptor(); TransactionManager transactionManager = beanFactory.getTransactionManager(); Transaction transaction = transactionManager.getTransactionQuietly(); TransactionContext nativeTransactionContext = transaction == null ? null : transaction.getTransactionContext(); response.setTransactionContext(nativeTransactionContext); try { transactionInterceptor.beforeSendResponse(response); } catch (RuntimeException rex) { logger.error("Error occurred in remote call!", rex); throw new RpcException("Error occurred in remote call!", rex); } } public Result consumerInvoke(Invoker invoker, Invocation invocation) throws RpcException { String interfaceClazz = RpcContext.getContext().getUrl().getServiceInterface(); boolean participantFlag = TransactionParticipant.class.getName().equals(interfaceClazz); boolean xaResourceFlag = XAResource.class.getName().equals(interfaceClazz); boolean coordinatorFlag = RemoteCoordinator.class.getName().equals(interfaceClazz); if (participantFlag == false && xaResourceFlag == false && coordinatorFlag == false) { return this.consumerInvokeForSVC(invoker, invocation); } else if (StringUtils.equals(invocation.getMethodName(), KEY_XA_RESOURCE_START)) { return this.consumerInvokeForKey(invoker, invocation); } else if (StringUtils.equals(invocation.getMethodName(), KEY_XA_GET_IDENTIFIER)) { return this.consumerInvokeForKey(invoker, invocation); } else if (StringUtils.equals(invocation.getMethodName(), KEY_XA_GET_APPLICATION)) { return this.consumerInvokeForKey(invoker, invocation); } else if (StringUtils.equals(invocation.getMethodName(), KEY_XA_GET_REMOTEADDR)) { return this.consumerInvokeForKey(invoker, invocation); } else if (StringUtils.equals(invocation.getMethodName(), KEY_XA_GET_REMOTENODE)) { return this.consumerInvokeForKey(invoker, invocation); } else { return this.consumerInvokeForJTA(invoker, invocation); } } public Result consumerInvokeForKey(Invoker invoker, Invocation invocation) throws RpcException { TransactionBeanRegistry beanRegistry = TransactionBeanRegistry.getInstance(); TransactionBeanFactory beanFactory = beanRegistry.getBeanFactory(); RemoteCoordinator transactionCoordinator = (RemoteCoordinator) beanFactory.getNativeParticipant(); Map attachments = invocation.getAttachments(); attachments.put(RemoteCoordinator.class.getName(), transactionCoordinator.getIdentifier()); RpcResult result = (RpcResult) invoker.invoke(invocation); Object value = result.getValue(); if (InvocationResult.class.isInstance(value)) { InvocationResult wrapped = (InvocationResult) value; result.setValue(null); result.setException(null); if (wrapped.isFailure()) { result.setException(wrapped.getError()); } else { result.setValue(wrapped.getValue()); } String instanceId = StringUtils.trimToEmpty(String.valueOf(wrapped.getVariable(RemoteCoordinator.class.getName()))); this.registerRemoteParticipantIfNecessary(instanceId); String interfaceClazz = RpcContext.getContext().getUrl().getServiceInterface(); boolean participantFlag = TransactionParticipant.class.getName().equals(interfaceClazz); boolean xaResourceFlag = XAResource.class.getName().equals(interfaceClazz); boolean coordinatorFlag = RemoteCoordinator.class.getName().equals(interfaceClazz); boolean resultInitRequired = (participantFlag || xaResourceFlag || coordinatorFlag) && result.getValue() == null; if (resultInitRequired) { if (StringUtils.equals(invocation.getMethodName(), KEY_XA_GET_IDENTIFIER)) { result.setValue(instanceId); } else if (StringUtils.equals(invocation.getMethodName(), KEY_XA_GET_APPLICATION)) { result.setValue(CommonUtils.getApplication(instanceId)); } else if (StringUtils.equals(invocation.getMethodName(), KEY_XA_GET_REMOTEADDR)) { result.setValue(CommonUtils.getRemoteAddr(instanceId)); } else if (StringUtils.equals(invocation.getMethodName(), KEY_XA_GET_REMOTENODE)) { result.setValue(CommonUtils.getRemoteNode(instanceId)); } } // end-if (resultInitRequired) } // end-if (InvocationResult.class.isInstance(value)) return result; } public Result consumerInvokeForJTA(Invoker invoker, Invocation invocation) throws RpcException { TransactionBeanRegistry beanRegistry = TransactionBeanRegistry.getInstance(); TransactionBeanFactory beanFactory = beanRegistry.getBeanFactory(); RemoteCoordinator transactionCoordinator = (RemoteCoordinator) beanFactory.getNativeParticipant(); Map attachments = invocation.getAttachments(); attachments.put(RemoteCoordinator.class.getName(), transactionCoordinator.getIdentifier()); RpcResult result = (RpcResult) invoker.invoke(invocation); Object value = result.getValue(); if (InvocationResult.class.isInstance(value)) { InvocationResult wrapped = (InvocationResult) value; result.setValue(null); result.setException(null); if (wrapped.isFailure()) { result.setException(wrapped.getError()); } else { result.setValue(wrapped.getValue()); } } // end-if (InvocationResult.class.isInstance(value)) return result; } public Result consumerInvokeForSVC(Invoker invoker, Invocation invocation) throws RpcException { RemoteCoordinatorRegistry participantRegistry = RemoteCoordinatorRegistry.getInstance(); TransactionBeanRegistry beanRegistry = TransactionBeanRegistry.getInstance(); TransactionBeanFactory beanFactory = beanRegistry.getBeanFactory(); RemoteCoordinator transactionCoordinator = (RemoteCoordinator) beanFactory.getNativeParticipant(); TransactionManager transactionManager = beanFactory.getTransactionManager(); Transaction transaction = transactionManager.getTransactionQuietly(); TransactionContext nativeTransactionContext = transaction == null ? null : transaction.getTransactionContext(); InvocationDef invocationDef = new InvocationDef(); invocationDef.setInterfaceClass(invoker.getInterface()); invocationDef.setMethodName(invocation.getMethodName()); invocationDef.setParameterTypes(invocation.getParameterTypes()); RemoteCoordinator participant = this.getParticipantByRemoteAddr(invoker, invocationDef); TransactionRequestImpl request = new TransactionRequestImpl(); request.setTransactionContext(nativeTransactionContext); request.setTargetTransactionCoordinator(participant); TransactionResponseImpl response = new TransactionResponseImpl(); response.setSourceTransactionCoordinator(participant); RpcResult result = null; RpcException invokeError = null; Throwable serverError = null; try { this.beforeConsumerInvokeForSVC(invocation, request, response); result = (RpcResult) invoker.invoke(invocation); Object value = result.getValue(); if (InvocationResult.class.isInstance(value)) { InvocationResult wrapped = (InvocationResult) value; result.setValue(null); result.setException(null); if (wrapped.isFailure()) { result.setException(wrapped.getError()); serverError = wrapped.getError(); } else { result.setValue(wrapped.getValue()); } String propagatedBy = (String) wrapped.getVariable(Propagation.class.getName()); String instanceId = (String) wrapped.getVariable(RemoteCoordinator.class.getName()); participantRegistry.putInvocationDef(invocationDef, CommonUtils.getRemoteNode(instanceId)); String identifier = transactionCoordinator.getIdentifier(); boolean participantDelistRequired = StringUtils.equals(propagatedBy, identifier) == false; response.setParticipantDelistFlag(participantDelistRequired); response.setParticipantEnlistFlag(request.isParticipantEnlistFlag()); } } catch (RpcException rex) { invokeError = rex; } catch (Throwable rex) { logger.error("Error occurred in remote call!", rex); invokeError = new RpcException(rex.getMessage()); } finally { try { this.afterConsumerInvokeForSVC(invocation, request, response); } catch (RpcException rex) { if (invokeError == null) { throw rex; } else { logger.error("Error occurred in remote call!", rex); throw invokeError; } } catch (RuntimeException rex) { if (invokeError == null) { throw new RpcException(rex.getMessage()); } else { logger.error("Error occurred in remote call!", rex); throw invokeError; } } } if (serverError == null && invokeError == null) { return result; } else if (serverError == null && invokeError != null) { throw invokeError; } else if (RpcException.class.isInstance(serverError)) { throw (RpcException) serverError; } else { return result; } } private RemoteCoordinator getParticipantByRemoteAddr(Invoker invoker, InvocationDef invocationDef) { RemoteCoordinatorRegistry participantRegistry = RemoteCoordinatorRegistry.getInstance(); URL targetUrl = invoker.getUrl(); String targetAddr = targetUrl.getIp(); int targetPort = targetUrl.getPort(); RemoteAddr remoteAddr = new RemoteAddr(); remoteAddr.setServerHost(targetAddr); remoteAddr.setServerPort(targetPort); if (participantRegistry.containsPhysicalInstance(remoteAddr) == false) { this.initializePhysicalInstanceIfNecessary(remoteAddr); } // end-if (participantRegistry.containsPhysicalInstance(remoteAddr) == false) RemoteNode invocationContext = new RemoteNode(); invocationContext.setServerHost(targetAddr); invocationContext.setServerPort(targetPort); RemoteCoordinator remoteCoordinator = participantRegistry.getPhysicalInstance(remoteAddr); DubboRemoteCoordinator dubboCoordinator = new DubboRemoteCoordinator(); dubboCoordinator.setInvocationContext(invocationContext); dubboCoordinator.setRemoteCoordinator(remoteCoordinator); dubboCoordinator.setCoordinatorType(DubboRemoteCoordinator.KEY_PARTICIPANT_TYPE_EXACT); RemoteCoordinator participant = (RemoteCoordinator) Proxy.newProxyInstance( DubboRemoteCoordinator.class.getClassLoader(), new Class[] { RemoteCoordinator.class }, dubboCoordinator); dubboCoordinator.setProxyCoordinator(participant); return participant; } private void beforeConsumerInvokeForSVC(Invocation invocation, TransactionRequestImpl request, TransactionResponseImpl response) { TransactionBeanRegistry beanRegistry = TransactionBeanRegistry.getInstance(); TransactionBeanFactory beanFactory = beanRegistry.getBeanFactory(); TransactionInterceptor transactionInterceptor = beanFactory.getTransactionInterceptor(); RemoteCoordinator transactionCoordinator = (RemoteCoordinator) beanFactory.getNativeParticipant(); Map attachments = invocation.getAttachments(); attachments.put(RemoteCoordinator.class.getName(), transactionCoordinator.getIdentifier()); transactionInterceptor.beforeSendRequest(request); if (request.getTransactionContext() != null) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); HessianOutput output = new HessianOutput(baos); try { output.writeObject(request.getTransactionContext()); } catch (IOException ex) { logger.error("Error occurred in remote call!", ex); throw new RpcException("Error occurred in remote call!", ex); } String transactionContextContent = ByteUtils.byteArrayToString(baos.toByteArray()); attachments.put(TransactionContext.class.getName(), transactionContextContent); } } private void afterConsumerInvokeForSVC(Invocation invocation, TransactionRequestImpl request, TransactionResponseImpl response) { TransactionBeanRegistry beanRegistry = TransactionBeanRegistry.getInstance(); TransactionBeanFactory beanFactory = beanRegistry.getBeanFactory(); TransactionInterceptor transactionInterceptor = beanFactory.getTransactionInterceptor(); RpcException rpcError = null; try { if (request.getTransactionContext() != null) { String transactionContextContent = invocation.getAttachment(TransactionContext.class.getName()); byte[] byteArray = ByteUtils.stringToByteArray(transactionContextContent); ByteArrayInputStream bais = new ByteArrayInputStream(byteArray); HessianInput input = new HessianInput(bais); TransactionContext remoteTransactionContext = (TransactionContext) input.readObject(); response.setTransactionContext(remoteTransactionContext); } } catch (IOException ex) { logger.error("Error occurred in remote call!", ex); rpcError = new RpcException("Error occurred in remote call!", ex); } try { transactionInterceptor.afterReceiveResponse(response); } catch (RuntimeException rex) { logger.error("Error occurred in remote call!", rex); throw new RpcException("Error occurred in remote call!", rex); } if (rpcError != null) { throw rpcError; } } private void registerRemoteParticipantIfNecessary(String instanceId) { RemoteCoordinatorRegistry participantRegistry = RemoteCoordinatorRegistry.getInstance(); RemoteAddr remoteAddr = CommonUtils.getRemoteAddr(instanceId); RemoteNode remoteNode = CommonUtils.getRemoteNode(instanceId); if (StringUtils.isNotBlank(instanceId) && remoteAddr != null && remoteNode != null && participantRegistry.containsRemoteNode(remoteAddr) == false) { participantRegistry.putRemoteNode(remoteAddr, remoteNode); } } private void initializePhysicalInstanceIfNecessary(RemoteAddr remoteAddr) throws RpcException { if (remoteAddr != null) { RemoteCoordinatorRegistry participantRegistry = RemoteCoordinatorRegistry.getInstance(); RemoteCoordinator physicalInst = participantRegistry.getPhysicalInstance(remoteAddr); if (physicalInst == null) { String serverHost = remoteAddr.getServerHost(); int serverPort = remoteAddr.getServerPort(); final String target = String.format("%s:%s", serverHost, serverPort).intern(); synchronized (target) { RemoteCoordinator participant = participantRegistry.getPhysicalInstance(remoteAddr); if (participant == null) { this.processInitPhysicalInstanceIfNecessary(remoteAddr); } } // end-synchronized (target) } // end-if (physicalInst == null) } } private void processInitPhysicalInstanceIfNecessary(RemoteAddr remoteAddr) throws RpcException { RemoteCoordinatorRegistry participantRegistry = RemoteCoordinatorRegistry.getInstance(); TransactionBeanRegistry beanRegistry = TransactionBeanRegistry.getInstance(); RemoteCoordinator participant = participantRegistry.getPhysicalInstance(remoteAddr); if (participant == null) { ApplicationConfig applicationConfig = beanRegistry.getBean(ApplicationConfig.class); RegistryConfig registryConfig = beanRegistry.getBean(RegistryConfig.class); ProtocolConfig protocolConfig = beanRegistry.getBean(ProtocolConfig.class); ReferenceConfig referenceConfig = new ReferenceConfig(); referenceConfig.setInterface(RemoteCoordinator.class); referenceConfig.setTimeout(15000); referenceConfig.setCluster("failfast"); referenceConfig.setLoadbalance("bytejta"); referenceConfig.setFilter("bytejta"); referenceConfig.setGroup("org-bytesoft-bytejta"); referenceConfig.setCheck(false); referenceConfig.setRetries(-1); referenceConfig.setUrl(String.format("%s:%s", remoteAddr.getServerHost(), remoteAddr.getServerPort())); referenceConfig.setScope(Constants.SCOPE_REMOTE); referenceConfig.setApplication(applicationConfig); if (registryConfig != null) { referenceConfig.setRegistry(registryConfig); } if (protocolConfig != null) { referenceConfig.setProtocol(protocolConfig.getName()); } // end-if (protocolConfig != null) RemoteCoordinator reference = referenceConfig.get(); if (reference == null) { throw new RpcException("Cannot get the application name of the remote application."); } participantRegistry.putPhysicalInstance(remoteAddr, reference); } } static class InvocationResult implements HessianHandle, Serializable { private static final long serialVersionUID = 1L; private Throwable error; private Object value; private final Map variables = new HashMap(); public boolean isFailure() { return this.error != null; } public Object getValue() { return value; } public void setValue(Object value) { this.value = value; } public void setVariable(String key, Serializable value) { this.variables.put(key, value); } public Serializable getVariable(String key) { return this.variables.get(key); } public Throwable getError() { return error; } public void setError(Throwable error) { this.error = error; } } } ================================================ FILE: bytejta-supports-dubbo/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter ================================================ bytejta=org.bytesoft.bytejta.supports.dubbo.spi.TransactionServiceFilter ================================================ FILE: bytejta-supports-dubbo/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.LoadBalance ================================================ bytejta=org.bytesoft.bytejta.supports.dubbo.spi.TransactionLoadBalance ================================================ FILE: bytejta-supports-dubbo/src/main/resources/META-INF/dubbo/org.bytesoft.bytejta.supports.dubbo.ext.ILoadBalancer ================================================ default=org.bytesoft.bytejta.supports.dubbo.spi.TransactionLoadBalancer ================================================ FILE: bytejta-supports-dubbo/src/main/resources/bytejta-supports-dubbo.xml ================================================ ================================================ FILE: bytejta-supports-springcloud/pom.xml ================================================ 4.0.0 org.bytesoft bytejta-parent 0.5.0-BETA9 bytejta-supports-springcloud jar bytejta-supports-springcloud The bytejta-supports project is the module of ByteJTA for integrating with third-party open source project. http://www.bytesoft.org UTF-8 org.bytesoft bytejta-supports javax.servlet javax.servlet-api org.springframework spring-context org.springframework spring-tx org.springframework spring-jdbc org.springframework spring-aop org.springframework spring-webmvc org.springframework.boot spring-boot-autoconfigure org.springframework.cloud spring-cloud-starter-netflix-eureka-server org.springframework.cloud spring-cloud-starter-netflix-ribbon io.netty netty-buffer io.netty netty-codec io.netty netty-codec-http io.netty netty-common io.netty netty-handler io.netty netty-transport io.netty netty-transport-native-epoll org.springframework.cloud spring-cloud-starter-netflix-hystrix org.springframework.cloud spring-cloud-netflix-core org.springframework.cloud spring-cloud-openfeign-core org.springframework.cloud spring-cloud-starter-openfeign org.springframework.cloud spring-cloud-commons org.springframework.cloud spring-cloud-zookeeper-core io.netty netty-buffer io.netty netty-codec io.netty netty-codec-http io.netty netty-common io.netty netty-handler io.netty netty-transport io.netty netty-transport-native-epoll aopalliance aopalliance cglib cglib asm asm org.ow2.asm asm org.aspectj aspectjweaver org.apache.commons commons-dbcp2 ================================================ FILE: bytejta-supports-springcloud/src/main/java/org/bytesoft/bytejta/supports/springcloud/SpringCloudBeanRegistry.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.springcloud; import java.lang.reflect.Proxy; import org.apache.commons.lang3.StringUtils; import org.bytesoft.bytejta.supports.internal.RemoteCoordinatorRegistry; import org.bytesoft.bytejta.supports.springcloud.loadbalancer.TransactionLoadBalancerInterceptor; import org.bytesoft.common.utils.CommonUtils; import org.bytesoft.transaction.TransactionBeanFactory; import org.bytesoft.transaction.aware.TransactionBeanFactoryAware; import org.bytesoft.transaction.remote.RemoteAddr; import org.bytesoft.transaction.remote.RemoteCoordinator; import org.bytesoft.transaction.remote.RemoteNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.EnvironmentAware; import org.springframework.core.env.Environment; import org.springframework.web.client.RestTemplate; public final class SpringCloudBeanRegistry implements TransactionBeanFactoryAware, EnvironmentAware { static final Logger logger = LoggerFactory.getLogger(SpringCloudBeanRegistry.class); private static final SpringCloudBeanRegistry instance = new SpringCloudBeanRegistry(); @javax.inject.Inject private TransactionBeanFactory beanFactory; private RestTemplate restTemplate; private ThreadLocal interceptors = new ThreadLocal(); private Environment environment; private SpringCloudBeanRegistry() { if (instance != null) { throw new IllegalStateException(); } } public static SpringCloudBeanRegistry getInstance() { return instance; } public RemoteCoordinator getConsumeCoordinator(String identifier) { RemoteCoordinatorRegistry registry = RemoteCoordinatorRegistry.getInstance(); if (StringUtils.isBlank(identifier)) { return null; } RemoteAddr remoteAddr = CommonUtils.getRemoteAddr(identifier); RemoteNode remoteNode = CommonUtils.getRemoteNode(identifier); RemoteNode targetNode = registry.getRemoteNode(remoteAddr); if (targetNode == null && remoteAddr != null // && remoteNode != null && StringUtils.isNotBlank(remoteNode.getServiceKey())) { registry.putRemoteNode(remoteAddr, remoteNode); } // String application = CommonUtils.getApplication(identifier); // RemoteCoordinator participant = registry.getParticipant(application); // if (participant != null) { // return participant; // } RemoteCoordinator physical = registry.getPhysicalInstance(remoteAddr); if (physical != null) { return physical; } SpringCloudCoordinator handler = new SpringCloudCoordinator(); handler.setIdentifier(identifier); handler.setEnvironment(this.environment); physical = (RemoteCoordinator) Proxy.newProxyInstance(SpringCloudCoordinator.class.getClassLoader(), new Class[] { RemoteCoordinator.class }, handler); registry.putPhysicalInstance(remoteAddr, physical); // registry.putRemoteNode(remoteAddr, remoteNode); // registry.putParticipant(application, participant); return physical; } public TransactionLoadBalancerInterceptor getLoadBalancerInterceptor() { return this.interceptors.get(); } public void setLoadBalancerInterceptor(TransactionLoadBalancerInterceptor interceptor) { this.interceptors.set(interceptor); } public void removeLoadBalancerInterceptor() { this.interceptors.remove(); } public RestTemplate getRestTemplate() { return restTemplate; } public void setRestTemplate(RestTemplate restTemplate) { this.restTemplate = restTemplate; } public void setBeanFactory(TransactionBeanFactory tbf) { this.beanFactory = tbf; } public TransactionBeanFactory getBeanFactory() { return beanFactory; } public Environment getEnvironment() { return environment; } public void setEnvironment(Environment environment) { this.environment = environment; } } ================================================ FILE: bytejta-supports-springcloud/src/main/java/org/bytesoft/bytejta/supports/springcloud/SpringCloudCoordinator.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.springcloud; import java.io.IOException; import java.io.Serializable; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; 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.common.utils.CommonUtils; import org.bytesoft.common.utils.SerializeUtils; import org.bytesoft.transaction.TransactionParticipant; 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.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.HttpServerErrorException; import org.springframework.web.client.RestTemplate; public class SpringCloudCoordinator implements InvocationHandler { static final Logger logger = LoggerFactory.getLogger(SpringCloudCoordinator.class); static final String CONSTANT_CONTENT_PATH = "org.bytesoft.bytejta.contextpath"; private String identifier; private Environment environment; public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Class clazz = method.getDeclaringClass(); String methodName = method.getName(); Class returnType = method.getReturnType(); if (Object.class.equals(clazz)) { return method.invoke(this, args); } else if (TransactionParticipant.class.equals(clazz)) { throw new XAException(XAException.XAER_RMFAIL); } else if (RemoteCoordinator.class.equals(clazz)) { if ("getIdentifier".equals(methodName)) { return this.identifier; } else if ("getApplication".equals(methodName)) { int firstIndex = this.identifier.indexOf(":"); int lastIndex = this.identifier.lastIndexOf(":"); return firstIndex <= 0 || lastIndex <= 0 || firstIndex > lastIndex // ? null : this.identifier.subSequence(firstIndex + 1, lastIndex); } else if ("getRemoteAddr".equals(methodName) && RemoteAddr.class.equals(returnType)) { return this.identifier == null ? null : CommonUtils.getRemoteAddr(this.identifier); } else if ("getRemoteNode".equals(methodName) && RemoteNode.class.equals(returnType)) { return this.identifier == null ? null : CommonUtils.getRemoteNode(this.identifier); } else { throw new XAException(XAException.XAER_RMFAIL); } } else if (XAResource.class.equals(clazz)) { if ("start".equals(methodName)) { return null; // return immediately } else if ("prepare".equals(methodName)) { return this.invokeHttpPostRequest(proxy, method, args); } else if ("commit".equals(methodName)) { return this.invokeHttpPostRequest(proxy, method, args); } else if ("rollback".equals(methodName)) { return this.invokeHttpPostRequest(proxy, method, args); } else if ("recover".equals(methodName)) { return this.invokeTransactionRecover(proxy, method, args); } else if ("forget".equals(methodName)) { return this.invokeHttpPostRequest(proxy, method, args); } else { throw new XAException(XAException.XAER_RMFAIL); } } else { throw new IllegalAccessException(); } } public Object invokeHttpPostRequest(Object proxy, Method method, Object[] args) throws Throwable { Class returnType = method.getReturnType(); try { RestTemplate transactionRestTemplate = SpringCloudBeanRegistry.getInstance().getRestTemplate(); RestTemplate restTemplate = transactionRestTemplate == null ? new RestTemplate() : transactionRestTemplate; RemoteNode remoteNode = CommonUtils.getRemoteNode(this.identifier); String contextPathKey = String.format("%s.%s", CONSTANT_CONTENT_PATH, remoteNode.getServiceKey()); String contextPath = StringUtils.isBlank(remoteNode.getServiceKey()) // ? null : StringUtils.trimToEmpty(this.environment.getProperty(contextPathKey)); StringBuilder ber = new StringBuilder(); ber.append("http://").append(remoteNode.getServerHost()).append(":").append(remoteNode.getServerPort()); if (StringUtils.isNotBlank(contextPath) || StringUtils.equals(contextPath, "/")) { ber.append(contextPath.startsWith("/") ? "" : "/").append(contextPath); } // end-if (StringUtils.isNotBlank(contextPath) || StringUtils.equals(contextPath, "/")) ber.append("/org/bytesoft/bytejta/"); ber.append(method.getName()); for (int i = 0; i < args.length; i++) { Serializable arg = (Serializable) args[i]; ber.append("/").append(this.serialize(arg)); } ResponseEntity response = restTemplate.postForEntity(ber.toString(), null, returnType, new Object[0]); return response.getBody(); } catch (HttpClientErrorException ex) { XAException xaEx = new XAException(XAException.XAER_RMFAIL); xaEx.initCause(ex); throw xaEx; } catch (HttpServerErrorException ex) { // int statusCode = ex.getRawStatusCode(); HttpHeaders headers = ex.getResponseHeaders(); String failureText = StringUtils.trimToNull(headers.getFirst("failure")); String errorText = StringUtils.trimToNull(headers.getFirst("XA_XAER")); Boolean failure = failureText == null ? null : Boolean.parseBoolean(failureText); Integer errorCode = null; try { errorCode = errorText == null ? null : Integer.parseInt(errorText); } catch (Exception ignore) { logger.debug(ignore.getMessage()); } if (failure != null && errorCode != null) { XAException xaEx = new XAException(errorCode); xaEx.initCause(ex); throw xaEx; } else { XAException xaEx = new XAException(XAException.XAER_RMERR); xaEx.initCause(ex); throw xaEx; } } catch (Exception ex) { XAException xaEx = new XAException(XAException.XAER_RMERR); xaEx.initCause(ex); throw xaEx; } } public Object invokeTransactionRecover(Object proxy, Method method, Object[] args) throws Throwable { try { RestTemplate transactionRestTemplate = SpringCloudBeanRegistry.getInstance().getRestTemplate(); RestTemplate restTemplate = transactionRestTemplate == null ? new RestTemplate() : transactionRestTemplate; RemoteNode remoteNode = CommonUtils.getRemoteNode(this.identifier); String contextPathKey = String.format("%s.%s", CONSTANT_CONTENT_PATH, remoteNode.getServiceKey()); String contextPath = StringUtils.isBlank(remoteNode.getServiceKey()) // ? null : StringUtils.trimToEmpty(this.environment.getProperty(contextPathKey)); StringBuilder ber = new StringBuilder(); ber.append("http://").append(remoteNode.getServerHost()).append(":").append(remoteNode.getServerPort()); if (StringUtils.isNotBlank(contextPath) || StringUtils.equals(contextPath, "/")) { ber.append(contextPath.startsWith("/") ? "" : "/").append(contextPath); } // end-if (StringUtils.isNotBlank(contextPath) || StringUtils.equals(contextPath, "/")) ber.append("/org/bytesoft/bytejta/"); ber.append(method.getName()); for (int i = 0; i < args.length; i++) { Serializable arg = (Serializable) args[i]; ber.append("/").append(this.serialize(arg)); } ResponseEntity response = restTemplate.getForEntity(ber.toString(), TransactionXid[].class, new Object[0]); return response.getBody(); } catch (HttpClientErrorException ex) { XAException xaEx = new XAException(XAException.XAER_RMFAIL); xaEx.initCause(ex); throw xaEx; } catch (HttpServerErrorException ex) { // int statusCode = ex.getRawStatusCode(); HttpHeaders headers = ex.getResponseHeaders(); String failureText = StringUtils.trimToNull(headers.getFirst("failure")); String errorText = StringUtils.trimToNull(headers.getFirst("XA_XAER")); Boolean failure = failureText == null ? null : Boolean.parseBoolean(failureText); Integer errorCode = null; try { errorCode = errorText == null ? null : Integer.parseInt(errorText); } catch (Exception ignore) { logger.debug(ignore.getMessage()); } if (failure != null && errorCode != null) { XAException xaEx = new XAException(errorCode); xaEx.initCause(ex); throw xaEx; } else { XAException xaEx = new XAException(XAException.XAER_RMERR); xaEx.initCause(ex); throw xaEx; } } catch (Exception ex) { XAException xaEx = new XAException(XAException.XAER_RMERR); xaEx.initCause(ex); throw xaEx; } } private String serialize(Serializable arg) throws IOException { if (Xid.class.isInstance(arg)) { Xid xid = (Xid) arg; byte[] globalTransactionId = xid.getGlobalTransactionId(); return ByteUtils.byteArrayToString(globalTransactionId); } else if (Integer.class.isInstance(arg) || Integer.TYPE.isInstance(arg)) { return String.valueOf(arg); } else if (Boolean.class.isInstance(arg) || Boolean.TYPE.isInstance(arg)) { return String.valueOf(arg); } else { byte[] byteArray = SerializeUtils.serializeObject(arg); return ByteUtils.byteArrayToString(byteArray); } } public String toString() { return String.format("", this.identifier); } public String getIdentifier() { return identifier; } public void setIdentifier(String identifier) { this.identifier = identifier; } public Environment getEnvironment() { return environment; } public void setEnvironment(Environment environment) { this.environment = environment; } } ================================================ FILE: bytejta-supports-springcloud/src/main/java/org/bytesoft/bytejta/supports/springcloud/SpringCloudEndpointPostProcessor.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.springcloud; import java.util.ArrayList; import java.util.List; import org.bytesoft.common.utils.CommonUtils; import org.bytesoft.transaction.TransactionBeanFactory; import org.bytesoft.transaction.aware.TransactionBeanFactoryAware; import org.bytesoft.transaction.aware.TransactionEndpointAware; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.EnvironmentAware; import org.springframework.core.env.Environment; public class SpringCloudEndpointPostProcessor implements BeanFactoryPostProcessor, TransactionBeanFactoryAware, EnvironmentAware { static final Logger logger = LoggerFactory.getLogger(SpringCloudEndpointPostProcessor.class); @javax.inject.Inject private TransactionBeanFactory beanFactory; private Environment environment; public void setEnvironment(Environment environment) { this.environment = environment; } public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { ClassLoader cl = Thread.currentThread().getContextClassLoader(); List beanDefList = new ArrayList(); String[] beanNameArray = beanFactory.getBeanDefinitionNames(); for (int i = 0; i < beanNameArray.length; i++) { String beanName = beanNameArray[i]; BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName); String beanClassName = beanDef.getBeanClassName(); Class beanClass = null; try { beanClass = cl.loadClass(beanClassName); } catch (Exception ex) { logger.debug("Cannot load class {}, beanId= {}!", beanClassName, beanName, ex); continue; } if (TransactionEndpointAware.class.isAssignableFrom(beanClass)) { beanDefList.add(beanDef); } } String host = CommonUtils.getInetAddress(); String name = this.environment.getProperty("spring.application.name"); String port = this.environment.getProperty("server.port"); String identifier = String.format("%s:%s:%s", host, name, port); for (int i = 0; i < beanDefList.size(); i++) { BeanDefinition beanDef = beanDefList.get(i); MutablePropertyValues mpv = beanDef.getPropertyValues(); mpv.addPropertyValue(TransactionEndpointAware.ENDPOINT_FIELD_NAME, identifier); } } public TransactionBeanFactory getBeanFactory() { return beanFactory; } public void setBeanFactory(TransactionBeanFactory beanFactory) { this.beanFactory = beanFactory; } } ================================================ FILE: bytejta-supports-springcloud/src/main/java/org/bytesoft/bytejta/supports/springcloud/config/SpringCloudConfiguration.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.springcloud.config; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import javax.transaction.UserTransaction; import org.apache.commons.lang3.StringUtils; import org.bytesoft.bytejta.supports.resource.properties.ConnectorResourcePropertySourceFactory; import org.bytesoft.bytejta.supports.springcloud.SpringCloudBeanRegistry; import org.bytesoft.bytejta.supports.springcloud.feign.TransactionClientRegistry; import org.bytesoft.bytejta.supports.springcloud.feign.TransactionFeignBeanPostProcessor; import org.bytesoft.bytejta.supports.springcloud.feign.TransactionFeignContract; import org.bytesoft.bytejta.supports.springcloud.feign.TransactionFeignDecoder; import org.bytesoft.bytejta.supports.springcloud.feign.TransactionFeignErrorDecoder; import org.bytesoft.bytejta.supports.springcloud.feign.TransactionFeignInterceptor; import org.bytesoft.bytejta.supports.springcloud.hystrix.TransactionHystrixBeanPostProcessor; import org.bytesoft.bytejta.supports.springcloud.loadbalancer.TransactionLoadBalancerRuleImpl; import org.bytesoft.bytejta.supports.springcloud.property.TransactionPropertySourceFactory; import org.bytesoft.bytejta.supports.springcloud.web.TransactionHandlerInterceptor; import org.bytesoft.bytejta.supports.springcloud.web.TransactionRequestInterceptor; import org.bytesoft.common.utils.CommonUtils; import org.bytesoft.transaction.TransactionManager; import org.bytesoft.transaction.aware.TransactionEndpointAware; import org.springframework.beans.BeansException; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.PropertyValue; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.http.HttpMessageConverters; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.cloud.openfeign.support.ResponseEntityDecoder; import org.springframework.cloud.openfeign.support.SpringDecoder; import org.springframework.cloud.openfeign.support.SpringMvcContract; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.EnvironmentAware; import org.springframework.context.annotation.DependsOn; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.context.annotation.ImportResource; import org.springframework.context.annotation.PropertySource; import org.springframework.core.env.Environment; import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.annotation.TransactionManagementConfigurer; import org.springframework.web.client.RestTemplate; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import feign.codec.ErrorDecoder; @PropertySource(value = "bytejta:loadbalancer.config", factory = TransactionPropertySourceFactory.class) @PropertySource(value = "bytejta:connector.config", factory = ConnectorResourcePropertySourceFactory.class) @ImportResource({ "classpath:bytejta-supports-springcloud.xml" }) @EnableAspectJAutoProxy(proxyTargetClass = true) @EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class }) @EnableTransactionManagement public class SpringCloudConfiguration implements WebMvcConfigurer, TransactionManagementConfigurer, BeanFactoryPostProcessor, InitializingBean, SmartInitializingSingleton, TransactionEndpointAware, EnvironmentAware, ApplicationContextAware { static final String CONSTANT_INCLUSIONS = "org.bytesoft.bytejta.feign.inclusions"; static final String CONSTANT_EXCLUSIONS = "org.bytesoft.bytejta.feign.exclusions"; static final String FEIGN_FACTORY_CLASS = "org.springframework.cloud.openfeign.FeignClientFactoryBean"; private ApplicationContext applicationContext; private String identifier; private Environment environment; private transient final Set transientClientSet = new HashSet(); public void afterSingletonsInstantiated() /* Check if the rule is set correctly */ { com.netflix.loadbalancer.IRule loadBalancerRule = null; try { loadBalancerRule = this.applicationContext.getBean(com.netflix.loadbalancer.IRule.class); } catch (NoSuchBeanDefinitionException ex) { return; // return quietly } if (TransactionLoadBalancerRuleImpl.class.isInstance(loadBalancerRule)) { return; // return quietly } throw new IllegalStateException("TransactionLoadBalancerRuleImpl is disabled!"); } public void afterPropertiesSet() throws Exception { String host = CommonUtils.getInetAddress(); String name = this.environment.getProperty("spring.application.name"); String port = this.environment.getProperty("server.port"); this.identifier = String.format("%s:%s:%s", host, name, port); } public PlatformTransactionManager annotationDrivenTransactionManager() { org.springframework.transaction.jta.JtaTransactionManager jtaTransactionManager // = new org.springframework.transaction.jta.JtaTransactionManager(); jtaTransactionManager.setTransactionManager(this.applicationContext.getBean(TransactionManager.class)); jtaTransactionManager.setUserTransaction(this.applicationContext.getBean(UserTransaction.class)); return jtaTransactionManager; } @org.springframework.context.annotation.Bean @ConditionalOnProperty(name = "feign.hystrix.enabled", havingValue = "false", matchIfMissing = true) public TransactionFeignBeanPostProcessor feignPostProcessor() { return new TransactionFeignBeanPostProcessor(); } @org.springframework.context.annotation.Bean @ConditionalOnProperty(name = "feign.hystrix.enabled") @ConditionalOnClass(feign.hystrix.HystrixFeign.class) public TransactionHystrixBeanPostProcessor hystrixPostProcessor() { return new TransactionHystrixBeanPostProcessor(); } @org.springframework.context.annotation.Bean public TransactionFeignInterceptor transactionFeignInterceptor() { TransactionFeignInterceptor interceptor = new TransactionFeignInterceptor(); interceptor.setEndpoint(this.identifier); return interceptor; } @org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean(feign.Contract.class) @org.springframework.context.annotation.Bean public feign.Contract feignContract() { return new SpringMvcContract(); } @org.springframework.context.annotation.Primary @org.springframework.context.annotation.Bean public feign.Contract transactionFeignContract(@Autowired feign.Contract contract) { return new TransactionFeignContract(contract); } @org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean(feign.codec.Decoder.class) @org.springframework.context.annotation.Bean public feign.codec.Decoder feignDecoder(@Autowired ObjectFactory messageConverters) { return new ResponseEntityDecoder(new SpringDecoder(messageConverters)); } @org.springframework.context.annotation.Primary @org.springframework.context.annotation.Bean public feign.codec.Decoder transactionFeignDecoder(@Autowired ObjectFactory objectFactory, @Autowired feign.codec.Decoder decoder) { TransactionFeignDecoder feignDecoder = new TransactionFeignDecoder(decoder); feignDecoder.setObjectFactory(objectFactory); return feignDecoder; } @org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean(feign.codec.ErrorDecoder.class) @org.springframework.context.annotation.Bean public feign.codec.ErrorDecoder errorDecoder() { return new ErrorDecoder.Default(); } @org.springframework.context.annotation.Primary @org.springframework.context.annotation.Bean public feign.codec.ErrorDecoder transactionErrorDecoder(@Autowired feign.codec.ErrorDecoder decoder) { return new TransactionFeignErrorDecoder(decoder); } @org.springframework.context.annotation.Bean public TransactionHandlerInterceptor transactionHandlerInterceptor() { TransactionHandlerInterceptor interceptor = new TransactionHandlerInterceptor(); interceptor.setEndpoint(this.identifier); return interceptor; } @org.springframework.context.annotation.Bean public TransactionRequestInterceptor transactionRequestInterceptor() { TransactionRequestInterceptor interceptor = new TransactionRequestInterceptor(); interceptor.setEndpoint(this.identifier); return interceptor; } @org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean(ClientHttpRequestFactory.class) @org.springframework.context.annotation.Bean @SuppressWarnings("deprecation") public ClientHttpRequestFactory defaultRequestFactory() { return new org.springframework.http.client.Netty4ClientHttpRequestFactory(); } @org.springframework.context.annotation.Bean("transactionRestTemplate") public RestTemplate transactionTemplate(@Autowired ClientHttpRequestFactory requestFactory) { RestTemplate restTemplate = new RestTemplate(); restTemplate.setRequestFactory(requestFactory); return restTemplate; } @DependsOn("transactionRestTemplate") @org.springframework.context.annotation.Bean public SpringCloudBeanRegistry beanRegistry(@Qualifier("transactionRestTemplate") @Autowired RestTemplate restTemplate) { SpringCloudBeanRegistry registry = SpringCloudBeanRegistry.getInstance(); registry.setRestTemplate(restTemplate); return registry; } @org.springframework.context.annotation.Primary @org.springframework.cloud.client.loadbalancer.LoadBalanced @org.springframework.context.annotation.Bean public RestTemplate defaultRestTemplate(@Autowired TransactionRequestInterceptor transactionRequestInterceptor) { RestTemplate restTemplate = new RestTemplate(); restTemplate.getInterceptors().add(transactionRequestInterceptor); return restTemplate; } public void addInterceptors(InterceptorRegistry registry) { TransactionHandlerInterceptor transactionHandlerInterceptor = this.applicationContext .getBean(TransactionHandlerInterceptor.class); registry.addInterceptor(transactionHandlerInterceptor); } public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { String[] beanNameArray = beanFactory.getBeanDefinitionNames(); for (int i = 0; i < beanNameArray.length; i++) { BeanDefinition beanDef = beanFactory.getBeanDefinition(beanNameArray[i]); String beanClassName = beanDef.getBeanClassName(); if (FEIGN_FACTORY_CLASS.equals(beanClassName) == false) { continue; } MutablePropertyValues mpv = beanDef.getPropertyValues(); PropertyValue pv = mpv.getPropertyValue("name"); String client = String.valueOf(pv.getValue() == null ? "" : pv.getValue()); if (StringUtils.isNotBlank(client)) { this.transientClientSet.add(client); } } this.fireAfterPropertiesSet(); } public void fireAfterPropertiesSet() { TransactionClientRegistry registry = TransactionClientRegistry.getInstance(); String inclusions = this.environment.getProperty(CONSTANT_INCLUSIONS); String exclusions = this.environment.getProperty(CONSTANT_EXCLUSIONS); if (StringUtils.isNotBlank(inclusions) && StringUtils.isNotBlank(exclusions)) { throw new IllegalStateException(String.format("Property '%s' and '%s' can not be configured together!", CONSTANT_INCLUSIONS, CONSTANT_EXCLUSIONS)); } else if (StringUtils.isNotBlank(inclusions)) { String[] clients = inclusions.split("\\s*,\\s*"); for (int i = 0; i < clients.length; i++) { String client = clients[i]; registry.registerClient(client); } // end-for (int i = 0; i < clients.length; i++) this.transientClientSet.clear(); } else if (StringUtils.isNotBlank(exclusions)) { String[] clients = exclusions.split("\\s*,\\s*"); for (int i = 0; i < clients.length; i++) { String client = clients[i]; this.transientClientSet.remove(client); } // end-for (int i = 0; i < clients.length; i++) Iterator itr = this.transientClientSet.iterator(); while (itr.hasNext()) { String client = itr.next(); itr.remove(); registry.registerClient(client); } // end-while (itr.hasNext()) } else { Iterator itr = this.transientClientSet.iterator(); while (itr.hasNext()) { String client = itr.next(); itr.remove(); registry.registerClient(client); } // end-while (itr.hasNext()) } } public String getEndpoint() { return this.identifier; } public void setEndpoint(String identifier) { this.identifier = identifier; } public void setEnvironment(Environment environment) { this.environment = environment; } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } } ================================================ FILE: bytejta-supports-springcloud/src/main/java/org/bytesoft/bytejta/supports/springcloud/controller/TransactionCoordinatorController.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.springcloud.controller; import java.beans.PropertyEditorSupport; import javax.servlet.http.HttpServletResponse; import javax.transaction.xa.XAException; import javax.transaction.xa.Xid; import org.bytesoft.bytejta.TransactionCoordinator; import org.bytesoft.common.utils.ByteUtils; import org.bytesoft.transaction.TransactionBeanFactory; import org.bytesoft.transaction.aware.TransactionBeanFactoryAware; import org.bytesoft.transaction.xa.XidFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class TransactionCoordinatorController extends PropertyEditorSupport implements TransactionBeanFactoryAware { static final Logger logger = LoggerFactory.getLogger(TransactionCoordinatorController.class); @Autowired private TransactionCoordinator transactionCoordinator; @Autowired private TransactionBeanFactory beanFactory; @RequestMapping(value = "/org/bytesoft/bytejta/prepare/{xid}", method = RequestMethod.POST, produces = { MediaType.APPLICATION_JSON_UTF8_VALUE }) @ResponseBody public int prepare(@PathVariable("xid") String identifier, HttpServletResponse response) { try { XidFactory xidFactory = this.beanFactory.getXidFactory(); byte[] byteArray = ByteUtils.stringToByteArray(identifier); Xid xid = xidFactory.createGlobalXid(byteArray); return this.transactionCoordinator.prepare(xid); } catch (XAException ex) { logger.error("Error occurred while preparing transaction: {}.", identifier, ex); response.addHeader("failure", "true"); response.addHeader("XA_XAER", String.valueOf(ex.errorCode)); response.setStatus(500); return -1; } catch (RuntimeException ex) { logger.error("Error occurred while preparing transaction: {}.", identifier, ex); response.addHeader("failure", "true"); response.setStatus(500); return -1; } } @RequestMapping(value = "/org/bytesoft/bytejta/commit/{xid}/{opc}", method = RequestMethod.POST) @ResponseBody public void commit(@PathVariable("xid") String identifier, @PathVariable("opc") boolean onePhase, HttpServletResponse response) { try { XidFactory xidFactory = this.beanFactory.getXidFactory(); byte[] byteArray = ByteUtils.stringToByteArray(identifier); Xid xid = xidFactory.createGlobalXid(byteArray); this.transactionCoordinator.commit(xid, onePhase); } catch (XAException ex) { logger.error("Error occurred while committing transaction: {}.", identifier, ex); response.addHeader("failure", "true"); response.addHeader("XA_XAER", String.valueOf(ex.errorCode)); response.setStatus(500); } catch (RuntimeException ex) { logger.error("Error occurred while committing transaction: {}.", identifier, ex); response.addHeader("failure", "true"); response.setStatus(500); } } @RequestMapping(value = "/org/bytesoft/bytejta/rollback/{xid}", method = RequestMethod.POST) @ResponseBody public void rollback(@PathVariable("xid") String identifier, HttpServletResponse response) { try { XidFactory xidFactory = this.beanFactory.getXidFactory(); byte[] byteArray = ByteUtils.stringToByteArray(identifier); Xid xid = xidFactory.createGlobalXid(byteArray); this.transactionCoordinator.rollback(xid); } catch (XAException ex) { logger.error("Error occurred while rolling back transaction: {}.", identifier, ex); response.addHeader("failure", "true"); response.addHeader("XA_XAER", String.valueOf(ex.errorCode)); response.setStatus(500); } catch (RuntimeException ex) { logger.error("Error occurred while rolling back transaction: {}.", identifier, ex); response.addHeader("failure", "true"); response.setStatus(500); } } @RequestMapping(value = "/org/bytesoft/bytejta/recover/{flag}", method = RequestMethod.GET) @ResponseBody public Xid[] recover(@PathVariable("flag") int flag, HttpServletResponse response) { try { return this.transactionCoordinator.recover(flag); } catch (XAException ex) { logger.error("Error occurred while recovering transactions.", ex); response.addHeader("failure", "true"); response.addHeader("XA_XAER", String.valueOf(ex.errorCode)); response.setStatus(500); return new Xid[0]; } catch (RuntimeException ex) { logger.error("Error occurred while recovering transactions.", ex); response.addHeader("failure", "true"); response.setStatus(500); return new Xid[0]; } } @RequestMapping(value = "/org/bytesoft/bytejta/forget/{xid}", method = RequestMethod.POST) @ResponseBody public void forget(@PathVariable("xid") String identifier, HttpServletResponse response) { try { XidFactory xidFactory = this.beanFactory.getXidFactory(); byte[] byteArray = ByteUtils.stringToByteArray(identifier); Xid xid = xidFactory.createGlobalXid(byteArray); this.transactionCoordinator.forget(xid); } catch (XAException ex) { logger.error("Error occurred while forgetting transaction: {}.", identifier, ex); response.addHeader("failure", "true"); response.addHeader("XA_XAER", String.valueOf(ex.errorCode)); response.setStatus(500); } catch (RuntimeException ex) { logger.error("Error occurred while forgetting transaction: {}.", identifier, ex); response.addHeader("failure", "true"); response.setStatus(500); } } public TransactionBeanFactory getBeanFactory() { return this.beanFactory; } public void setBeanFactory(TransactionBeanFactory tbf) { this.beanFactory = tbf; } } ================================================ FILE: bytejta-supports-springcloud/src/main/java/org/bytesoft/bytejta/supports/springcloud/dbcp/CommonDBCPXADataSourceWrapper.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.bytejta.supports.springcloud.dbcp; import javax.sql.DataSource; import javax.sql.XADataSource; import org.apache.commons.dbcp2.managed.BasicManagedDataSource; import org.bytesoft.transaction.TransactionBeanFactory; import org.bytesoft.transaction.TransactionManager; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.jdbc.XADataSourceWrapper; public class CommonDBCPXADataSourceWrapper implements XADataSourceWrapper { @Autowired private TransactionBeanFactory beanFactory; public DataSource wrapDataSource(XADataSource xaDataSource) throws Exception { TransactionManager transactionManager = this.beanFactory.getTransactionManager(); BasicManagedDataSource bds = new BasicManagedDataSource(); bds.setXaDataSourceInstance(xaDataSource); bds.setTransactionManager(transactionManager); return bds; } } ================================================ FILE: bytejta-supports-springcloud/src/main/java/org/bytesoft/bytejta/supports/springcloud/feign/TransactionClientRegistry.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.springcloud.feign; import java.util.HashSet; import java.util.Set; public class TransactionClientRegistry { private static final TransactionClientRegistry instance = new TransactionClientRegistry(); private final Set clients = new HashSet(); private TransactionClientRegistry() { if (instance != null) { throw new IllegalStateException(); } } public static TransactionClientRegistry getInstance() { return instance; } public void registerClient(String name) { this.clients.add(name); } public void unRegisterClient(String name) { this.clients.remove(name); } public boolean containsClient(String name) { return this.clients.contains(name); } } ================================================ FILE: bytejta-supports-springcloud/src/main/java/org/bytesoft/bytejta/supports/springcloud/feign/TransactionFeignBeanPostProcessor.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.bytejta.supports.springcloud.feign; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import org.apache.commons.lang3.StringUtils; import org.springframework.aop.TargetSource; import org.springframework.aop.target.SingletonTargetSource; import org.springframework.beans.BeansException; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.cloud.openfeign.FeignClient; public class TransactionFeignBeanPostProcessor implements BeanPostProcessor, InitializingBean { static final String FEIGN_CLAZZ_NAME = "feign.ReflectiveFeign$FeignInvocationHandler"; private Field singletonTargetSourceTargetField = null; public void afterPropertiesSet() throws Exception { Field field = SingletonTargetSource.class.getDeclaredField("target"); field.setAccessible(true); this.singletonTargetSourceTargetField = field; } public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (Proxy.isProxyClass(bean.getClass()) == false) { return bean; } TargetSource targetSource = null; Object object = bean; if (org.springframework.aop.framework.Advised.class.isInstance(bean)) { org.springframework.aop.framework.Advised advised = (org.springframework.aop.framework.Advised) bean; Class[] interfaces = advised.getProxiedInterfaces(); for (int i = 0; i < interfaces.length; i++) { Class intf = interfaces[i]; if (intf.getAnnotation(FeignClient.class) != null) { targetSource = advised.getTargetSource(); try { object = targetSource.getTarget(); break; } catch (Exception error) { throw new IllegalStateException(); } } // end-if (intf.getAnnotation(FeignClient.class) != null) } // end-for (int i = 0; i < interfaces.length; i++) } // end-if (org.springframework.aop.framework.Advised.class.isInstance(bean)) InvocationHandler handler = Proxy.getInvocationHandler(object); if (targetSource == null // && StringUtils.equals(FEIGN_CLAZZ_NAME, handler.getClass().getName()) == false) { return bean; } Object proxied = this.createProxiedObject(object); if (targetSource == null) { return proxied; } if (SingletonTargetSource.class.isInstance(targetSource)) { try { this.singletonTargetSourceTargetField.set(targetSource, proxied); } catch (IllegalArgumentException error) { throw new IllegalStateException("Error occurred!"); } catch (IllegalAccessException error) { throw new IllegalStateException("Error occurred!"); } } else { throw new IllegalStateException("Not supported yet!"); } return bean; } private Object createProxiedObject(Object origin) { InvocationHandler handler = Proxy.getInvocationHandler(origin); TransactionFeignHandler feignHandler = new TransactionFeignHandler(); feignHandler.setDelegate(handler); Class clazz = origin.getClass(); Class[] interfaces = clazz.getInterfaces(); ClassLoader loader = clazz.getClassLoader(); return Proxy.newProxyInstance(loader, interfaces, feignHandler); } } ================================================ FILE: bytejta-supports-springcloud/src/main/java/org/bytesoft/bytejta/supports/springcloud/feign/TransactionFeignContract.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.springcloud.feign; import java.util.List; import org.springframework.beans.BeansException; import org.springframework.beans.factory.InitializingBean; import org.springframework.cloud.openfeign.support.SpringMvcContract; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import feign.MethodMetadata; public class TransactionFeignContract implements feign.Contract, InitializingBean, ApplicationContextAware { private ApplicationContext applicationContext; private feign.Contract delegate; public TransactionFeignContract() { } public TransactionFeignContract(feign.Contract contract) { this.delegate = contract; } public void afterPropertiesSet() throws Exception { if (this.delegate == null) { this.invokeAfterPropertiesSet(); } // end-if (this.delegate == null) } public void invokeAfterPropertiesSet() throws Exception { feign.Contract feignContract = null; String[] beanNameArray = this.applicationContext.getBeanNamesForType(feign.Contract.class); for (int i = 0; beanNameArray != null && i < beanNameArray.length; i++) { String beanName = beanNameArray[i]; Object beanInst = this.applicationContext.getBean(beanName); if (TransactionFeignContract.class.isInstance(beanInst)) { continue; } else if (feignContract != null) { throw new RuntimeException("There are more than one feign.Contract exists!"); } else { feignContract = (feign.Contract) beanInst; } } if (feignContract == null) { feignContract = new SpringMvcContract(); } // end-if (feignContract == null) this.delegate = feignContract; } public List parseAndValidatateMetadata(Class targetType) { List metas = this.delegate.parseAndValidatateMetadata(targetType); for (int i = 0; i < metas.size(); i++) { MethodMetadata meta = metas.get(i); if (meta.returnType() == void.class) { meta.returnType(String.class); } } return metas; } public feign.Contract getDelegate() { return delegate; } public void setDelegate(feign.Contract delegate) { this.delegate = delegate; } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } } ================================================ FILE: bytejta-supports-springcloud/src/main/java/org/bytesoft/bytejta/supports/springcloud/feign/TransactionFeignDecoder.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.springcloud.feign; import java.io.IOException; import java.lang.reflect.Type; import java.util.Base64; import java.util.Collection; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.bytesoft.bytejta.supports.rpc.TransactionResponseImpl; import org.bytesoft.bytejta.supports.springcloud.SpringCloudBeanRegistry; import org.bytesoft.common.utils.SerializeUtils; import org.bytesoft.transaction.TransactionContext; import org.bytesoft.transaction.remote.RemoteCoordinator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.ObjectFactory; import org.springframework.boot.autoconfigure.http.HttpMessageConverters; import org.springframework.cloud.openfeign.support.ResponseEntityDecoder; import org.springframework.cloud.openfeign.support.SpringDecoder; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import feign.FeignException; import feign.Request; import feign.Response; import feign.codec.DecodeException; public class TransactionFeignDecoder implements feign.codec.Decoder, InitializingBean, ApplicationContextAware { static Logger logger = LoggerFactory.getLogger(TransactionFeignDecoder.class); static final String HEADER_TRANCACTION_KEY = "X-BYTEJTA-TRANSACTION"; // org.bytesoft.bytejta.transaction static final String HEADER_PROPAGATION_KEY = "X-BYTEJTA-PROPAGATION"; // org.bytesoft.bytejta.propagation private ApplicationContext applicationContext; private feign.codec.Decoder delegate; private ObjectFactory objectFactory; public TransactionFeignDecoder() { } public TransactionFeignDecoder(feign.codec.Decoder decoder) { this.delegate = decoder; } public void afterPropertiesSet() throws Exception { if (this.delegate == null) { this.invokeAfterPropertiesSet(); } // end-if (this.delegate == null) } public void invokeAfterPropertiesSet() throws Exception { feign.codec.Decoder feignDecoder = null; String[] beanNameArray = this.applicationContext.getBeanNamesForType(feign.codec.Decoder.class); for (int i = 0; beanNameArray != null && i < beanNameArray.length; i++) { String beanName = beanNameArray[i]; Object beanInst = this.applicationContext.getBean(beanName); if (TransactionFeignDecoder.class.isInstance(beanInst)) { continue; } else if (feignDecoder != null) { throw new RuntimeException("There are more than one feign.codec.Decoder exists!"); } else { feignDecoder = (feign.codec.Decoder) beanInst; } } if (feignDecoder == null) { feignDecoder = new ResponseEntityDecoder(new SpringDecoder(this.objectFactory)); } // end-if (feignDecoder == null) this.delegate = feignDecoder; } public Object decode(Response resp, Type type) throws IOException, DecodeException, FeignException { Request request = resp.request(); String reqTransactionStr = this.getHeaderValue(request, HEADER_TRANCACTION_KEY); String reqPropagationStr = this.getHeaderValue(request, HEADER_PROPAGATION_KEY); String respTransactionStr = this.getHeaderValue(resp, HEADER_TRANCACTION_KEY); String respPropagationStr = this.getHeaderValue(resp, HEADER_PROPAGATION_KEY); if (StringUtils.isBlank(reqTransactionStr)) { return this.delegate.decode(resp, type); } else if (StringUtils.isBlank(reqPropagationStr)) { return this.delegate.decode(resp, type); } try { String transactionStr = StringUtils.isBlank(respTransactionStr) ? reqTransactionStr : respTransactionStr; String propagationStr = StringUtils.isBlank(respPropagationStr) ? reqPropagationStr : respPropagationStr; byte[] byteArray = Base64.getDecoder().decode(transactionStr); // ByteUtils.stringToByteArray(transactionStr); TransactionContext transactionContext = (TransactionContext) SerializeUtils.deserializeObject(byteArray); SpringCloudBeanRegistry beanRegistry = SpringCloudBeanRegistry.getInstance(); RemoteCoordinator remoteCoordinator = beanRegistry.getConsumeCoordinator(propagationStr); TransactionResponseImpl response = new TransactionResponseImpl(); response.setTransactionContext(transactionContext); response.setSourceTransactionCoordinator(remoteCoordinator); } catch (IOException ex) { logger.error("Error occurred while decoding response({})!", resp, ex); } return this.delegate.decode(resp, type); } private String getHeaderValue(Request req, String headerName) { Map> headers = req.headers(); Collection values = headers.get(headerName); String value = null; if (values != null && values.isEmpty() == false) { String[] array = new String[values.size()]; values.toArray(array); value = array[0]; } return value; } private String getHeaderValue(Response resp, String headerName) { Map> headers = resp.headers(); Collection values = headers.get(headerName); String value = null; if (values != null && values.isEmpty() == false) { String[] array = new String[values.size()]; values.toArray(array); value = array[0]; } return value; } public feign.codec.Decoder getDelegate() { return delegate; } public void setDelegate(feign.codec.Decoder delegate) { this.delegate = delegate; } public ObjectFactory getObjectFactory() { return objectFactory; } public void setObjectFactory(ObjectFactory objectFactory) { this.objectFactory = objectFactory; } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } } ================================================ FILE: bytejta-supports-springcloud/src/main/java/org/bytesoft/bytejta/supports/springcloud/feign/TransactionFeignErrorDecoder.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.springcloud.feign; import java.io.IOException; import java.util.Base64; import java.util.Collection; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.bytesoft.bytejta.supports.rpc.TransactionResponseImpl; import org.bytesoft.bytejta.supports.springcloud.SpringCloudBeanRegistry; import org.bytesoft.common.utils.SerializeUtils; import org.bytesoft.transaction.TransactionContext; import org.bytesoft.transaction.remote.RemoteCoordinator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import feign.Request; import feign.Response; import feign.codec.ErrorDecoder; public class TransactionFeignErrorDecoder implements feign.codec.ErrorDecoder, InitializingBean, ApplicationContextAware { static Logger logger = LoggerFactory.getLogger(TransactionFeignErrorDecoder.class); static final String HEADER_TRANCACTION_KEY = "X-BYTEJTA-TRANSACTION"; // org.bytesoft.bytejta.transaction static final String HEADER_PROPAGATION_KEY = "X-BYTEJTA-PROPAGATION"; // org.bytesoft.bytejta.propagation private ApplicationContext applicationContext; private feign.codec.ErrorDecoder delegate; public TransactionFeignErrorDecoder() { } public TransactionFeignErrorDecoder(feign.codec.ErrorDecoder decoder) { this.delegate = decoder; } public void afterPropertiesSet() throws Exception { if (this.delegate == null) { this.invokeAfterPropertiesSet(); } // end-if (this.delegate == null) } public void invokeAfterPropertiesSet() throws Exception { feign.codec.ErrorDecoder errorDecoder = null; String[] beanNameArray = this.applicationContext.getBeanNamesForType(feign.codec.ErrorDecoder.class); for (int i = 0; beanNameArray != null && i < beanNameArray.length; i++) { String beanName = beanNameArray[i]; Object beanInst = this.applicationContext.getBean(beanName); if (TransactionFeignErrorDecoder.class.isInstance(beanInst)) { continue; } else if (errorDecoder != null) { throw new RuntimeException("There are more than one feign.codec.ErrorDecoder exists!"); } else { errorDecoder = (feign.codec.ErrorDecoder) beanInst; } } if (errorDecoder == null) { errorDecoder = new ErrorDecoder.Default(); } // end-if (errorDecoder == null) this.delegate = errorDecoder; } public Exception decode(String methodKey, Response resp) { Request request = resp.request(); String reqTransactionStr = this.getHeaderValue(request, HEADER_TRANCACTION_KEY); String reqPropagationStr = this.getHeaderValue(request, HEADER_PROPAGATION_KEY); String respTransactionStr = this.getHeaderValue(resp, HEADER_TRANCACTION_KEY); String respPropagationStr = this.getHeaderValue(resp, HEADER_PROPAGATION_KEY); if (StringUtils.isBlank(reqTransactionStr)) { return this.delegate.decode(methodKey, resp); } else if (StringUtils.isBlank(reqPropagationStr)) { return this.delegate.decode(methodKey, resp); } // int status = resp.status(); try { String transactionStr = StringUtils.isBlank(respTransactionStr) ? reqTransactionStr : respTransactionStr; String propagationStr = StringUtils.isBlank(respPropagationStr) ? reqPropagationStr : respPropagationStr; byte[] byteArray = Base64.getDecoder().decode(transactionStr); // ByteUtils.stringToByteArray(transactionStr); TransactionContext transactionContext = (TransactionContext) SerializeUtils.deserializeObject(byteArray); SpringCloudBeanRegistry beanRegistry = SpringCloudBeanRegistry.getInstance(); RemoteCoordinator remoteCoordinator = beanRegistry.getConsumeCoordinator(propagationStr); TransactionResponseImpl response = new TransactionResponseImpl(); response.setTransactionContext(transactionContext); response.setSourceTransactionCoordinator(remoteCoordinator); } catch (IOException ex) { logger.error("Error occurred while decoding response: methodKey= {}, response= {}", methodKey, resp, ex); } return this.delegate.decode(methodKey, resp); } private String getHeaderValue(Request req, String headerName) { Map> headers = req.headers(); Collection values = headers.get(headerName); String value = null; if (values != null && values.isEmpty() == false) { String[] array = new String[values.size()]; values.toArray(array); value = array[0]; } return value; } private String getHeaderValue(Response resp, String headerName) { Map> headers = resp.headers(); Collection values = headers.get(headerName); String value = null; if (values != null && values.isEmpty() == false) { String[] array = new String[values.size()]; values.toArray(array); value = array[0]; } return value; } public feign.codec.ErrorDecoder getDelegate() { return delegate; } public void setDelegate(feign.codec.ErrorDecoder delegate) { this.delegate = delegate; } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } } ================================================ FILE: bytejta-supports-springcloud/src/main/java/org/bytesoft/bytejta/supports/springcloud/feign/TransactionFeignHandler.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.springcloud.feign; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bytesoft.bytejta.TransactionImpl; import org.bytesoft.bytejta.supports.rpc.TransactionRequestImpl; import org.bytesoft.bytejta.supports.rpc.TransactionResponseImpl; import org.bytesoft.bytejta.supports.springcloud.SpringCloudBeanRegistry; import org.bytesoft.bytejta.supports.springcloud.loadbalancer.TransactionLoadBalancerInterceptor; import org.bytesoft.common.utils.CommonUtils; import org.bytesoft.transaction.TransactionBeanFactory; import org.bytesoft.transaction.TransactionContext; import org.bytesoft.transaction.TransactionManager; import org.bytesoft.transaction.archive.XAResourceArchive; import org.bytesoft.transaction.remote.RemoteCoordinator; import org.bytesoft.transaction.remote.RemoteSvc; import org.bytesoft.transaction.supports.rpc.TransactionInterceptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.netflix.appinfo.InstanceInfo; import com.netflix.loadbalancer.Server; import com.netflix.loadbalancer.Server.MetaInfo; import com.netflix.niws.loadbalancer.DiscoveryEnabledServer; public class TransactionFeignHandler implements InvocationHandler { static final Logger logger = LoggerFactory.getLogger(TransactionFeignHandler.class); private InvocationHandler delegate; public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } else { final SpringCloudBeanRegistry beanRegistry = SpringCloudBeanRegistry.getInstance(); TransactionBeanFactory beanFactory = beanRegistry.getBeanFactory(); TransactionManager transactionManager = beanFactory.getTransactionManager(); final TransactionInterceptor transactionInterceptor = beanFactory.getTransactionInterceptor(); TransactionImpl transaction = // (TransactionImpl) transactionManager.getTransactionQuietly(); if (transaction == null) { return this.delegate.invoke(proxy, method, args); } final TransactionContext transactionContext = transaction.getTransactionContext(); final TransactionRequestImpl request = new TransactionRequestImpl(); final TransactionResponseImpl response = new TransactionResponseImpl(); final Map participants = transaction.getRemoteParticipantMap(); beanRegistry.setLoadBalancerInterceptor(new TransactionLoadBalancerInterceptor() { public List beforeCompletion(List servers) { final List readyServerList = new ArrayList(); final List unReadyServerList = new ArrayList(); for (int i = 0; servers != null && i < servers.size(); i++) { Server server = servers.get(i); // String instanceId = metaInfo.getInstanceId(); String instanceId = null; if (DiscoveryEnabledServer.class.isInstance(server)) { DiscoveryEnabledServer discoveryEnabledServer = (DiscoveryEnabledServer) server; InstanceInfo instanceInfo = discoveryEnabledServer.getInstanceInfo(); String addr = instanceInfo.getIPAddr(); String appName = instanceInfo.getAppName(); int port = instanceInfo.getPort(); instanceId = String.format("%s:%s:%s", addr, appName, port); } else { MetaInfo metaInfo = server.getMetaInfo(); String host = server.getHost(); String addr = host.matches("\\d+(\\.\\d+){3}") ? host : CommonUtils.getInetAddress(host); String appName = metaInfo.getAppName(); int port = server.getPort(); instanceId = String.format("%s:%s:%s", addr, appName, port); } if (participants.containsKey(instanceId)) { List serverList = new ArrayList(); serverList.add(server); return serverList; } // end-if (participants.containsKey(instanceId)) if (server.isReadyToServe()) { readyServerList.add(server); } else { unReadyServerList.add(server); } } logger.warn("There is no suitable server: expect= {}, actual= {}!", participants.keySet(), servers); return readyServerList.isEmpty() ? unReadyServerList : readyServerList; } public void afterCompletion(Server server) { beanRegistry.removeLoadBalancerInterceptor(); if (server == null) { logger.warn( "There is no suitable server, the TransactionInterceptor.beforeSendRequest() operation is not executed!"); return; } // end-if (server == null) // TransactionRequestImpl request = new TransactionRequestImpl(); request.setTransactionContext(transactionContext); // String instanceId = metaInfo.getInstanceId(); String instanceId = null; if (DiscoveryEnabledServer.class.isInstance(server)) { DiscoveryEnabledServer discoveryEnabledServer = (DiscoveryEnabledServer) server; InstanceInfo instanceInfo = discoveryEnabledServer.getInstanceInfo(); String addr = instanceInfo.getIPAddr(); String appName = instanceInfo.getAppName(); int port = instanceInfo.getPort(); instanceId = String.format("%s:%s:%s", addr, appName, port); } else { MetaInfo metaInfo = server.getMetaInfo(); String host = server.getHost(); String addr = host.matches("\\d+(\\.\\d+){3}") ? host : CommonUtils.getInetAddress(host); String appName = metaInfo.getAppName(); int port = server.getPort(); instanceId = String.format("%s:%s:%s", addr, appName, port); } RemoteCoordinator coordinator = beanRegistry.getConsumeCoordinator(instanceId); request.setTargetTransactionCoordinator(coordinator); transactionInterceptor.beforeSendRequest(request); } }); try { return this.delegate.invoke(proxy, method, args); } finally { Object interceptedValue = response.getHeader(TransactionInterceptor.class.getName()); if (Boolean.valueOf(String.valueOf(interceptedValue)) == false) { response.setTransactionContext(transactionContext); RemoteCoordinator coordinator = request.getTargetTransactionCoordinator(); response.setSourceTransactionCoordinator(coordinator); response.setParticipantEnlistFlag(request.isParticipantEnlistFlag()); transactionInterceptor.afterReceiveResponse(response); } // end-if (response.isIntercepted() == false) } } } public InvocationHandler getDelegate() { return delegate; } public void setDelegate(InvocationHandler delegate) { this.delegate = delegate; } } ================================================ FILE: bytejta-supports-springcloud/src/main/java/org/bytesoft/bytejta/supports/springcloud/feign/TransactionFeignInterceptor.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.springcloud.feign; import java.io.IOException; import java.util.Base64; import java.util.Collection; import java.util.Map; import org.bytesoft.bytejta.supports.springcloud.SpringCloudBeanRegistry; import org.bytesoft.common.utils.SerializeUtils; import org.bytesoft.transaction.Transaction; import org.bytesoft.transaction.TransactionBeanFactory; import org.bytesoft.transaction.TransactionContext; import org.bytesoft.transaction.TransactionManager; import org.bytesoft.transaction.aware.TransactionEndpointAware; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; public class TransactionFeignInterceptor implements feign.RequestInterceptor, TransactionEndpointAware, ApplicationContextAware { static final String HEADER_TRANCACTION_KEY = "X-BYTEJTA-TRANSACTION"; // org.bytesoft.bytejta.transaction static final String HEADER_PROPAGATION_KEY = "X-BYTEJTA-PROPAGATION"; // org.bytesoft.bytejta.propagation private String identifier; private ApplicationContext applicationContext; public void apply(feign.RequestTemplate template) { final SpringCloudBeanRegistry beanRegistry = SpringCloudBeanRegistry.getInstance(); TransactionBeanFactory beanFactory = beanRegistry.getBeanFactory(); TransactionManager transactionManager = beanFactory.getTransactionManager(); Transaction transaction = transactionManager.getTransactionQuietly(); if (transaction == null) { return; } try { TransactionContext transactionContext = transaction.getTransactionContext(); byte[] byteArray = SerializeUtils.serializeObject(transactionContext); String transactionText = Base64.getEncoder().encodeToString(byteArray); Map> headers = template.headers(); if (headers.containsKey(HEADER_TRANCACTION_KEY) == false) { template.header(HEADER_TRANCACTION_KEY, transactionText); } if (headers.containsKey(HEADER_PROPAGATION_KEY) == false) { template.header(HEADER_PROPAGATION_KEY, identifier); } } catch (IOException ex) { throw new RuntimeException("Error occurred while preparing the transaction context!", ex); } } public String getEndpoint() { return this.identifier; } public void setEndpoint(String identifier) { this.identifier = identifier; } public ApplicationContext getApplicationContext() { return applicationContext; } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } } ================================================ FILE: bytejta-supports-springcloud/src/main/java/org/bytesoft/bytejta/supports/springcloud/hystrix/TransactionHystrixBeanPostProcessor.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.bytejta.supports.springcloud.hystrix; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Iterator; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.springframework.aop.TargetSource; import org.springframework.aop.target.SingletonTargetSource; import org.springframework.beans.BeansException; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.cloud.openfeign.FeignClient; import com.netflix.hystrix.HystrixCommand.Setter; import com.netflix.hystrix.HystrixCommandGroupKey; import com.netflix.hystrix.HystrixCommandKey; import feign.InvocationHandlerFactory.MethodHandler; import feign.Target; import feign.hystrix.FallbackFactory; public class TransactionHystrixBeanPostProcessor implements BeanPostProcessor, InitializingBean { static final String HYSTRIX_COMMAND_NAME = "TransactionHystrixInvocationHandler#invoke(TransactionHystrixInvocation)"; static final String HYSTRIX_INVOKER_NAME = "invoke"; static final String HYSTRIX_FIELD_CONSTANT = "constant"; static final String HYSTRIX_FIELD_TARGET = "target"; static final String HYSTRIX_FIELD_FACTORY = "fallbackFactory"; static final String HYSTRIX_FIELD_FALLBACK = "fallbackMethodMap"; static final String HYSTRIX_FIELD_DISPATH = "dispatch"; static final String HYSTRIX_FIELD_SETTERS = "setterMethodMap"; static final String HYSTRIX_SETTER_GRPKEY = "groupKey"; static final String HYSTRIX_CLAZZ_NAME = "feign.hystrix.HystrixInvocationHandler"; private Field singletonTargetSourceTargetField = null; public void afterPropertiesSet() throws Exception { Field field = SingletonTargetSource.class.getDeclaredField("target"); field.setAccessible(true); this.singletonTargetSourceTargetField = field; } public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (Proxy.isProxyClass(bean.getClass()) == false) { return bean; } TargetSource targetSource = null; Object object = bean; if (org.springframework.aop.framework.Advised.class.isInstance(bean)) { org.springframework.aop.framework.Advised advised = (org.springframework.aop.framework.Advised) bean; Class[] interfaces = advised.getProxiedInterfaces(); for (int i = 0; i < interfaces.length; i++) { Class intf = interfaces[i]; if (intf.getAnnotation(FeignClient.class) != null) { targetSource = advised.getTargetSource(); try { object = targetSource.getTarget(); break; } catch (Exception error) { throw new IllegalStateException(); } } // end-if (intf.getAnnotation(FeignClient.class) != null) } // end-for (int i = 0; i < interfaces.length; i++) } // end-if (org.springframework.aop.framework.Advised.class.isInstance(bean)) InvocationHandler handler = Proxy.getInvocationHandler(object); if (targetSource == null // && StringUtils.equals(HYSTRIX_CLAZZ_NAME, handler.getClass().getName()) == false) { return bean; } Object proxied = this.createProxiedObject(object); if (targetSource == null) { return proxied; } if (SingletonTargetSource.class.isInstance(targetSource)) { try { this.singletonTargetSourceTargetField.set(targetSource, proxied); } catch (IllegalArgumentException error) { throw new IllegalStateException("Error occurred!"); } catch (IllegalAccessException error) { throw new IllegalStateException("Error occurred!"); } } else { throw new IllegalStateException("Not supported yet!"); } return bean; } @SuppressWarnings("unchecked") private Object createProxiedObject(Object origin) { InvocationHandler handler = Proxy.getInvocationHandler(origin); final TransactionHystrixFeignHandler feignHandler = new TransactionHystrixFeignHandler(); feignHandler.setDelegate(handler); Class clazz = origin.getClass(); Class[] interfaces = clazz.getInterfaces(); ClassLoader loader = clazz.getClassLoader(); try { Field dispatchField = handler.getClass().getDeclaredField(HYSTRIX_FIELD_DISPATH); dispatchField.setAccessible(true); Map dispatch = (Map) dispatchField.get(handler); Field setterField = handler.getClass().getDeclaredField(HYSTRIX_FIELD_SETTERS); setterField.setAccessible(true); Map setterMap = (Map) setterField.get(handler); Field groupKeyField = Setter.class.getDeclaredField(HYSTRIX_SETTER_GRPKEY); groupKeyField.setAccessible(true); Field fallbackField = handler.getClass().getDeclaredField(HYSTRIX_FIELD_FALLBACK); fallbackField.setAccessible(true); Map fallbackMap = (Map) fallbackField.get(handler); Field targetField = handler.getClass().getDeclaredField(HYSTRIX_FIELD_TARGET); targetField.setAccessible(true); Target target = (Target) targetField.get(handler); Field factoryField = handler.getClass().getDeclaredField(HYSTRIX_FIELD_FACTORY); factoryField.setAccessible(true); FallbackFactory factory = (FallbackFactory) factoryField.get(handler); if (factory != null) { if (FallbackFactory.Default.class.isInstance(factory)) { Field constantField = FallbackFactory.Default.class.getDeclaredField(HYSTRIX_FIELD_CONSTANT); constantField.setAccessible(true); Object constant = constantField.get(factory); TransactionHystrixFallbackHandler fallback = new TransactionHystrixFallbackHandler(constant); Object proxy = Proxy.newProxyInstance(constant.getClass().getClassLoader(), new Class[] { TransactionHystrixInvocationHandler.class, target.type() }, fallback); constantField.set(factory, proxy); } else { TransactionHystrixFallbackFactoryHandler factoryHandler = new TransactionHystrixFallbackFactoryHandler( factory, target.type()); FallbackFactory proxy = (FallbackFactory) Proxy.newProxyInstance( factory.getClass().getClassLoader(), new Class[] { FallbackFactory.class }, factoryHandler); factoryField.set(handler, proxy); } } // end-if (factory != null) HystrixCommandGroupKey hystrixCommandGroupKey = null; for (Iterator> itr = setterMap.entrySet() .iterator(); hystrixCommandGroupKey == null && itr.hasNext();) { Map.Entry entry = itr.next(); Setter setter = entry.getValue(); hystrixCommandGroupKey = setter == null ? hystrixCommandGroupKey : (HystrixCommandGroupKey) groupKeyField.get(setter); } final String commandGroupKeyName = hystrixCommandGroupKey == null ? null : hystrixCommandGroupKey.name(); HystrixCommandGroupKey groupKey = new HystrixCommandGroupKey() { public String name() { return commandGroupKeyName; } }; HystrixCommandKey commandKey = new HystrixCommandKey() { public String name() { return HYSTRIX_COMMAND_NAME; } }; Setter setter = Setter.withGroupKey(groupKey).andCommandKey(commandKey); Method key = TransactionHystrixInvocationHandler.class.getDeclaredMethod(HYSTRIX_INVOKER_NAME, new Class[] { TransactionHystrixInvocation.class }); setterMap.put(key, setter); dispatch.put(key, new TransactionHystrixMethodHandler(dispatch)); fallbackMap.put(key, key); } catch (Exception ex) { throw new IllegalStateException("Error occurred!"); } return Proxy.newProxyInstance(loader, interfaces, feignHandler); } } ================================================ FILE: bytejta-supports-springcloud/src/main/java/org/bytesoft/bytejta/supports/springcloud/hystrix/TransactionHystrixFallbackFactoryHandler.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.bytejta.supports.springcloud.hystrix; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import feign.hystrix.FallbackFactory; public class TransactionHystrixFallbackFactoryHandler implements InvocationHandler { private final FallbackFactory fallbackFactory; private final Class fallbackType; public TransactionHystrixFallbackFactoryHandler(FallbackFactory fallbackFactory, Class fallbackType) { this.fallbackFactory = fallbackFactory; this.fallbackType = fallbackType; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object fallback = method.invoke(this.fallbackFactory, args); TransactionHystrixFallbackHandler fallbackHandler = new TransactionHystrixFallbackHandler(fallback); ClassLoader classLoader = fallback.getClass().getClassLoader(); Class[] interfaces = new Class[] { TransactionHystrixInvocationHandler.class, this.fallbackType }; return Proxy.newProxyInstance(classLoader, interfaces, fallbackHandler); } public FallbackFactory getFallbackFactory() { return fallbackFactory; } } ================================================ FILE: bytejta-supports-springcloud/src/main/java/org/bytesoft/bytejta/supports/springcloud/hystrix/TransactionHystrixFallbackHandler.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.bytejta.supports.springcloud.hystrix; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class TransactionHystrixFallbackHandler implements InvocationHandler { private final Object fallback; public TransactionHystrixFallbackHandler(Object fallback) { this.fallback = fallback; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { TransactionHystrixInvocation invocation = (TransactionHystrixInvocation) args[0]; Method targetMethod = invocation.getMethod(); // (Method) args[1]; Object[] targetArgs = invocation.getArgs(); // (Object[]) args[2]; return targetMethod.invoke(this.fallback, targetArgs); } public Object getFallback() { return fallback; } } ================================================ FILE: bytejta-supports-springcloud/src/main/java/org/bytesoft/bytejta/supports/springcloud/hystrix/TransactionHystrixFeignHandler.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.bytejta.supports.springcloud.hystrix; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import org.bytesoft.bytejta.TransactionImpl; import org.bytesoft.bytejta.supports.springcloud.SpringCloudBeanRegistry; import org.bytesoft.transaction.TransactionBeanFactory; import org.bytesoft.transaction.TransactionManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class TransactionHystrixFeignHandler implements InvocationHandler { static final Logger logger = LoggerFactory.getLogger(TransactionHystrixFeignHandler.class); private InvocationHandler delegate; public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (Object.class.equals(method.getDeclaringClass())) { return this.delegate.invoke(proxy, method, args); } else { final SpringCloudBeanRegistry beanRegistry = SpringCloudBeanRegistry.getInstance(); TransactionBeanFactory beanFactory = beanRegistry.getBeanFactory(); TransactionManager transactionManager = beanFactory.getTransactionManager(); TransactionImpl transaction = // (TransactionImpl) transactionManager.getTransactionQuietly(); if (transaction == null) { return this.delegate.invoke(proxy, method, args); } else { Method targetMethod = TransactionHystrixInvocationHandler.class.getDeclaredMethod( TransactionHystrixBeanPostProcessor.HYSTRIX_INVOKER_NAME, new Class[] { TransactionHystrixInvocation.class }); TransactionHystrixInvocation invocation = new TransactionHystrixInvocation(); invocation.setThread(Thread.currentThread()); invocation.setMethod(method); invocation.setArgs(args); Object[] targetArgs = new Object[] { invocation }; return this.delegate.invoke(proxy, targetMethod, targetArgs); } } } public InvocationHandler getDelegate() { return delegate; } public void setDelegate(InvocationHandler delegate) { this.delegate = delegate; } } ================================================ FILE: bytejta-supports-springcloud/src/main/java/org/bytesoft/bytejta/supports/springcloud/hystrix/TransactionHystrixInvocation.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.bytejta.supports.springcloud.hystrix; import java.lang.reflect.Method; public class TransactionHystrixInvocation { private Thread thread; private Method method; private Object[] args; public Thread getThread() { return thread; } public void setThread(Thread thread) { this.thread = thread; } public Method getMethod() { return method; } public void setMethod(Method method) { this.method = method; } public Object[] getArgs() { return args; } public void setArgs(Object[] args) { this.args = args; } } ================================================ FILE: bytejta-supports-springcloud/src/main/java/org/bytesoft/bytejta/supports/springcloud/hystrix/TransactionHystrixInvocationHandler.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.bytejta.supports.springcloud.hystrix; public interface TransactionHystrixInvocationHandler { public Object invoke(TransactionHystrixInvocation invocation); } ================================================ FILE: bytejta-supports-springcloud/src/main/java/org/bytesoft/bytejta/supports/springcloud/hystrix/TransactionHystrixMethodHandler.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.bytejta.supports.springcloud.hystrix; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.bytesoft.bytejta.TransactionImpl; import org.bytesoft.bytejta.supports.rpc.TransactionRequestImpl; import org.bytesoft.bytejta.supports.rpc.TransactionResponseImpl; import org.bytesoft.bytejta.supports.springcloud.SpringCloudBeanRegistry; import org.bytesoft.bytejta.supports.springcloud.loadbalancer.TransactionLoadBalancerInterceptor; import org.bytesoft.common.utils.CommonUtils; import org.bytesoft.transaction.TransactionBeanFactory; import org.bytesoft.transaction.TransactionContext; import org.bytesoft.transaction.TransactionManager; import org.bytesoft.transaction.archive.XAResourceArchive; import org.bytesoft.transaction.remote.RemoteCoordinator; import org.bytesoft.transaction.remote.RemoteSvc; import org.bytesoft.transaction.supports.rpc.TransactionInterceptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.netflix.appinfo.InstanceInfo; import com.netflix.loadbalancer.Server; import com.netflix.loadbalancer.Server.MetaInfo; import com.netflix.niws.loadbalancer.DiscoveryEnabledServer; import feign.InvocationHandlerFactory.MethodHandler; public class TransactionHystrixMethodHandler implements MethodHandler { static final Logger logger = LoggerFactory.getLogger(TransactionHystrixMethodHandler.class); private final Map dispatch; public TransactionHystrixMethodHandler(Map handlerMap) { this.dispatch = handlerMap; } public Object invoke(Object[] argv) throws Throwable { final SpringCloudBeanRegistry beanRegistry = SpringCloudBeanRegistry.getInstance(); TransactionBeanFactory beanFactory = beanRegistry.getBeanFactory(); TransactionManager transactionManager = beanFactory.getTransactionManager(); final TransactionInterceptor transactionInterceptor = beanFactory.getTransactionInterceptor(); TransactionHystrixInvocation invocation = (TransactionHystrixInvocation) argv[0]; Thread thread = invocation.getThread(); // (Thread) argv[0]; Method method = invocation.getMethod(); // (Method) argv[1]; Object[] args = invocation.getArgs(); // (Object[]) argv[2]; TransactionImpl transaction = // (TransactionImpl) transactionManager.getTransaction(thread); if (transaction == null) { return this.dispatch.get(method).invoke(args); } final TransactionContext transactionContext = transaction.getTransactionContext(); final TransactionRequestImpl request = new TransactionRequestImpl(); final TransactionResponseImpl response = new TransactionResponseImpl(); final Map participants = transaction.getRemoteParticipantMap(); beanRegistry.setLoadBalancerInterceptor(new TransactionLoadBalancerInterceptor() { public List beforeCompletion(List servers) { final List readyServerList = new ArrayList(); final List unReadyServerList = new ArrayList(); for (int i = 0; servers != null && i < servers.size(); i++) { Server server = servers.get(i); // String instanceId = metaInfo.getInstanceId(); String instanceId = null; if (DiscoveryEnabledServer.class.isInstance(server)) { DiscoveryEnabledServer discoveryEnabledServer = (DiscoveryEnabledServer) server; InstanceInfo instanceInfo = discoveryEnabledServer.getInstanceInfo(); String addr = instanceInfo.getIPAddr(); String appName = instanceInfo.getAppName(); int port = instanceInfo.getPort(); instanceId = String.format("%s:%s:%s", addr, appName, port); } else { MetaInfo metaInfo = server.getMetaInfo(); String host = server.getHost(); String addr = host.matches("\\d+(\\.\\d+){3}") ? host : CommonUtils.getInetAddress(host); String appName = metaInfo.getAppName(); int port = server.getPort(); instanceId = String.format("%s:%s:%s", addr, appName, port); } if (participants.containsKey(instanceId)) { List serverList = new ArrayList(); serverList.add(server); return serverList; } // end-if (participants.containsKey(instanceId)) if (server.isReadyToServe()) { readyServerList.add(server); } else { unReadyServerList.add(server); } } logger.warn("There is no suitable server: expect= {}, actual= {}!", participants.keySet(), servers); return readyServerList.isEmpty() ? unReadyServerList : readyServerList; } public void afterCompletion(Server server) { beanRegistry.removeLoadBalancerInterceptor(); if (server == null) { logger.warn( "There is no suitable server, the TransactionInterceptor.beforeSendRequest() operation is not executed!"); return; } // end-if (server == null) request.setTransactionContext(transactionContext); // String instanceId = metaInfo.getInstanceId(); String instanceId = null; if (DiscoveryEnabledServer.class.isInstance(server)) { DiscoveryEnabledServer discoveryEnabledServer = (DiscoveryEnabledServer) server; InstanceInfo instanceInfo = discoveryEnabledServer.getInstanceInfo(); String addr = instanceInfo.getIPAddr(); String appName = instanceInfo.getAppName(); int port = instanceInfo.getPort(); instanceId = String.format("%s:%s:%s", addr, appName, port); } else { MetaInfo metaInfo = server.getMetaInfo(); String host = server.getHost(); String addr = host.matches("\\d+(\\.\\d+){3}") ? host : CommonUtils.getInetAddress(host); String appName = metaInfo.getAppName(); int port = server.getPort(); instanceId = String.format("%s:%s:%s", addr, appName, port); } RemoteCoordinator coordinator = beanRegistry.getConsumeCoordinator(instanceId); request.setTargetTransactionCoordinator(coordinator); transactionInterceptor.beforeSendRequest(request); } }); try { transactionManager.associateThread(transaction); return this.dispatch.get(method).invoke(args); } finally { try { Object interceptedValue = response.getHeader(TransactionInterceptor.class.getName()); if (Boolean.valueOf(String.valueOf(interceptedValue)) == false) { response.setTransactionContext(transactionContext); RemoteCoordinator coordinator = request.getTargetTransactionCoordinator(); response.setSourceTransactionCoordinator(coordinator); response.setParticipantEnlistFlag(request.isParticipantEnlistFlag()); transactionInterceptor.afterReceiveResponse(response); } // end-if (response.isIntercepted() == false) } finally { transactionManager.desociateThread(); } } } public Map getDispatch() { return dispatch; } } ================================================ FILE: bytejta-supports-springcloud/src/main/java/org/bytesoft/bytejta/supports/springcloud/loadbalancer/TransactionLoadBalancerInterceptor.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.springcloud.loadbalancer; import java.util.List; import com.netflix.loadbalancer.Server; public interface TransactionLoadBalancerInterceptor { public List beforeCompletion(List servers); public void afterCompletion(Server server); } ================================================ FILE: bytejta-supports-springcloud/src/main/java/org/bytesoft/bytejta/supports/springcloud/loadbalancer/TransactionLoadBalancerRuleImpl.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.springcloud.loadbalancer; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.bytesoft.bytejta.supports.springcloud.SpringCloudBeanRegistry; import org.bytesoft.bytejta.supports.springcloud.rule.TransactionRule; import org.bytesoft.bytejta.supports.springcloud.rule.TransactionRuleImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.AbstractLoadBalancerRule; import com.netflix.loadbalancer.ILoadBalancer; import com.netflix.loadbalancer.IRule; import com.netflix.loadbalancer.Server; public class TransactionLoadBalancerRuleImpl extends AbstractLoadBalancerRule implements IRule { static final String CONSTANT_RULE_KEY = "org.bytesoft.bytejta.NFTransactionRuleClassName"; static Logger logger = LoggerFactory.getLogger(TransactionLoadBalancerRuleImpl.class); static Class transactionRuleClass; private IClientConfig clientConfig; public Server choose(Object key) { SpringCloudBeanRegistry registry = SpringCloudBeanRegistry.getInstance(); TransactionLoadBalancerInterceptor interceptor = registry.getLoadBalancerInterceptor(); if (transactionRuleClass == null) { Environment environment = registry.getEnvironment(); String clazzName = environment.getProperty(CONSTANT_RULE_KEY); if (StringUtils.isNotBlank(clazzName)) { try { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); transactionRuleClass = classLoader.loadClass(clazzName); } catch (Exception ex) { logger.error("Error occurred while loading class {}.", clazzName, ex); transactionRuleClass = TransactionRuleImpl.class; } } else { transactionRuleClass = TransactionRuleImpl.class; } } TransactionRule transactionRule = null; if (TransactionRuleImpl.class.equals(transactionRuleClass)) { transactionRule = new TransactionRuleImpl(); } else { try { transactionRule = (TransactionRule) transactionRuleClass.newInstance(); } catch (Exception ex) { logger.error("Can not create an instance of class {}.", transactionRuleClass.getName(), ex); transactionRule = new TransactionRuleImpl(); } } transactionRule.initWithNiwsConfig(this.clientConfig); transactionRule.setLoadBalancer(this.getLoadBalancer()); if (interceptor == null) { return transactionRule.chooseServer(key); } // end-if (interceptor == null) ILoadBalancer loadBalancer = this.getLoadBalancer(); List servers = loadBalancer.getAllServers(); Server server = null; try { List serverList = interceptor.beforeCompletion(servers); server = transactionRule.chooseServer(key, serverList); } finally { interceptor.afterCompletion(server); } return server; } public void initWithNiwsConfig(IClientConfig clientConfig) { this.clientConfig = clientConfig; } public IClientConfig getClientConfig() { return clientConfig; } } ================================================ FILE: bytejta-supports-springcloud/src/main/java/org/bytesoft/bytejta/supports/springcloud/property/TransactionPropertySource.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.springcloud.property; import org.apache.commons.lang3.StringUtils; import org.bytesoft.bytejta.supports.springcloud.feign.TransactionClientRegistry; import org.bytesoft.bytejta.supports.springcloud.loadbalancer.TransactionLoadBalancerRuleImpl; import org.springframework.core.env.PropertySource; import org.springframework.core.io.AbstractResource; import org.springframework.core.io.support.EncodedResource; public class TransactionPropertySource extends PropertySource { private boolean enabled; public TransactionPropertySource(String name, EncodedResource source) { super(name, source); EncodedResource encoded = (EncodedResource) this.getSource(); AbstractResource resource = (AbstractResource) encoded.getResource(); String path = resource.getFilename(); if (StringUtils.isBlank(path)) { return; } String[] values = path.split(":"); if (values.length != 2) { return; } String protocol = values[0]; String resName = values[1]; if ("bytejta".equalsIgnoreCase(protocol) == false) { return; } else if ("loadbalancer.config".equalsIgnoreCase(resName) == false) { return; } this.enabled = true; } public Object getProperty(String name) { if (this.enabled == false || name == null || StringUtils.isBlank(name) || name.indexOf(".") < 0) { return null; } TransactionClientRegistry registry = TransactionClientRegistry.getInstance(); int index = name.indexOf("."); String client = name.substring(0, index); String suffix = name.substring(index); if (registry.containsClient(client) && ".ribbon.NFLoadBalancerRuleClassName".equals(suffix)) { return TransactionLoadBalancerRuleImpl.class.getName(); } return null; } } ================================================ FILE: bytejta-supports-springcloud/src/main/java/org/bytesoft/bytejta/supports/springcloud/property/TransactionPropertySourceFactory.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.springcloud.property; import java.io.IOException; import org.springframework.core.env.PropertySource; import org.springframework.core.io.support.EncodedResource; import org.springframework.core.io.support.PropertySourceFactory; public class TransactionPropertySourceFactory implements PropertySourceFactory { public PropertySource createPropertySource(String name, EncodedResource resource) throws IOException { if (name == null) { name = String.format("%s@%s", TransactionPropertySource.class, System.identityHashCode(resource)); } // end-if (name == null) return new TransactionPropertySource(name, resource); } } ================================================ FILE: bytejta-supports-springcloud/src/main/java/org/bytesoft/bytejta/supports/springcloud/rule/TransactionRule.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.springcloud.rule; import java.util.List; import com.netflix.client.IClientConfigAware; import com.netflix.loadbalancer.ILoadBalancer; import com.netflix.loadbalancer.Server; public interface TransactionRule extends IClientConfigAware { public Server chooseServer(Object key, List serverList); public Server chooseServer(Object key); public ILoadBalancer getLoadBalancer(); public void setLoadBalancer(ILoadBalancer loadBalancer); } ================================================ FILE: bytejta-supports-springcloud/src/main/java/org/bytesoft/bytejta/supports/springcloud/rule/TransactionRuleImpl.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.springcloud.rule; import java.util.List; import java.util.Random; import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.ILoadBalancer; import com.netflix.loadbalancer.Server; public class TransactionRuleImpl implements TransactionRule { static final Random RANDOM = new Random(); private IClientConfig clientConfig; private ILoadBalancer loadBalancer; public void initWithNiwsConfig(IClientConfig clientConfig) { this.clientConfig = clientConfig; } public Server chooseServer(Object key, List serverList) { if (serverList == null || serverList.isEmpty()) { return null; } else if (serverList.size() == 1) { return serverList.get(0); } else { return serverList.get(RANDOM.nextInt(serverList.size())); } } public Server chooseServer(Object key) { List reachableServers = this.loadBalancer.getReachableServers(); List allServers = this.loadBalancer.getAllServers(); if (reachableServers != null && reachableServers.isEmpty() == false) { return reachableServers.get(RANDOM.nextInt(reachableServers.size())); } else if (allServers != null && allServers.isEmpty() == false) { return allServers.get(RANDOM.nextInt(allServers.size())); } else { return null; } } public IClientConfig getClientConfig() { return clientConfig; } public ILoadBalancer getLoadBalancer() { return this.loadBalancer; } public void setLoadBalancer(ILoadBalancer loadBalancer) { this.loadBalancer = loadBalancer; } } ================================================ FILE: bytejta-supports-springcloud/src/main/java/org/bytesoft/bytejta/supports/springcloud/serialize/XAResourceDeserializerImpl.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.springcloud.serialize; import java.lang.reflect.Proxy; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.bytesoft.bytejta.supports.internal.RemoteCoordinatorRegistry; import org.bytesoft.bytejta.supports.resource.RemoteResourceDescriptor; import org.bytesoft.bytejta.supports.springcloud.SpringCloudCoordinator; 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.supports.resource.XAResourceDescriptor; import org.bytesoft.transaction.supports.serialize.XAResourceDeserializer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.EnvironmentAware; import org.springframework.core.env.Environment; public class XAResourceDeserializerImpl implements XAResourceDeserializer, ApplicationContextAware, EnvironmentAware { static final Logger logger = LoggerFactory.getLogger(XAResourceDeserializerImpl.class); static Pattern pattern = Pattern.compile("^[^:]+\\s*:\\s*[^:]+\\s*:\\s*\\d+$"); private XAResourceDeserializer resourceDeserializer; private Environment environment; private ApplicationContext applicationContext; public XAResourceDescriptor deserialize(String identifier) { XAResourceDescriptor resourceDescriptor = this.resourceDeserializer.deserialize(identifier); if (resourceDescriptor != null) { return resourceDescriptor; } Matcher matcher = pattern.matcher(identifier); if (matcher.find() == false) { logger.error("can not find a matching xa-resource(identifier= {})!", identifier); return null; } RemoteCoordinatorRegistry registry = RemoteCoordinatorRegistry.getInstance(); RemoteAddr remoteAddr = CommonUtils.getRemoteAddr(identifier); RemoteNode remoteNode = CommonUtils.getRemoteNode(identifier); RemoteNode targetNode = registry.getRemoteNode(remoteAddr); if (targetNode == null && remoteAddr != null && remoteNode != null) { registry.putRemoteNode(remoteAddr, remoteNode); } // String application = CommonUtils.getApplication(identifier); // RemoteCoordinator participant = StringUtils.isBlank(application) ? null : registry.getParticipant(application); RemoteCoordinator physical = registry.getPhysicalInstance(remoteAddr); if (physical == null) { SpringCloudCoordinator springCloudCoordinator = new SpringCloudCoordinator(); springCloudCoordinator.setIdentifier(identifier); springCloudCoordinator.setEnvironment(this.environment); physical = (RemoteCoordinator) Proxy.newProxyInstance(SpringCloudCoordinator.class.getClassLoader(), new Class[] { RemoteCoordinator.class }, springCloudCoordinator); registry.putPhysicalInstance(remoteAddr, physical); } RemoteResourceDescriptor descriptor = new RemoteResourceDescriptor(); descriptor.setIdentifier(identifier); descriptor.setDelegate(registry.getPhysicalInstance(remoteAddr)); // descriptor.setDelegate(registry.getParticipant(application)); return descriptor; } public XAResourceDeserializer getResourceDeserializer() { return resourceDeserializer; } public void setResourceDeserializer(XAResourceDeserializer resourceDeserializer) { this.resourceDeserializer = resourceDeserializer; } public void setEnvironment(Environment environment) { this.environment = environment; } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } public ApplicationContext getApplicationContext() { return applicationContext; } } ================================================ FILE: bytejta-supports-springcloud/src/main/java/org/bytesoft/bytejta/supports/springcloud/web/TransactionHandlerInterceptor.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.springcloud.web; import java.util.Base64; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.StringUtils; import org.bytesoft.bytejta.supports.rpc.TransactionRequestImpl; import org.bytesoft.bytejta.supports.rpc.TransactionResponseImpl; import org.bytesoft.bytejta.supports.springcloud.SpringCloudBeanRegistry; import org.bytesoft.bytejta.supports.springcloud.controller.TransactionCoordinatorController; import org.bytesoft.common.utils.SerializeUtils; import org.bytesoft.transaction.Transaction; import org.bytesoft.transaction.TransactionBeanFactory; import org.bytesoft.transaction.TransactionContext; import org.bytesoft.transaction.TransactionManager; import org.bytesoft.transaction.aware.TransactionEndpointAware; import org.bytesoft.transaction.supports.rpc.TransactionInterceptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.boot.web.servlet.error.ErrorController; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; public class TransactionHandlerInterceptor implements HandlerInterceptor, TransactionEndpointAware, ApplicationContextAware { private static final Logger logger = LoggerFactory.getLogger(TransactionHandlerInterceptor.class); static final String HEADER_TRANCACTION_KEY = "X-BYTEJTA-TRANSACTION"; // org.bytesoft.bytejta.transaction static final String HEADER_PROPAGATION_KEY = "X-BYTEJTA-PROPAGATION"; // org.bytesoft.bytejta.propagation private String identifier; private ApplicationContext applicationContext; public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (HandlerMethod.class.isInstance(handler) == false) { logger.warn("TransactionHandlerInterceptor cannot handle current request(uri= {}, handler= {}) correctly.", request.getRequestURI(), handler); return true; } HandlerMethod hm = (HandlerMethod) handler; Class clazz = hm.getBeanType(); if (TransactionCoordinatorController.class.equals(clazz)) { return true; } else if (ErrorController.class.isInstance(hm.getBean())) { return true; } String transactionStr = request.getHeader(HEADER_TRANCACTION_KEY); if (StringUtils.isBlank(transactionStr)) { return true; } String propagationStr = request.getHeader(HEADER_PROPAGATION_KEY); String transactionText = StringUtils.trimToNull(transactionStr); String propagationText = StringUtils.trimToNull(propagationStr); SpringCloudBeanRegistry beanRegistry = SpringCloudBeanRegistry.getInstance(); TransactionBeanFactory beanFactory = beanRegistry.getBeanFactory(); TransactionInterceptor transactionInterceptor = beanFactory.getTransactionInterceptor(); byte[] byteArray = transactionText == null ? new byte[0] : Base64.getDecoder().decode(transactionText); TransactionContext transactionContext = null; if (byteArray != null && byteArray.length > 0) { transactionContext = (TransactionContext) SerializeUtils.deserializeObject(byteArray); transactionContext.setPropagated(true); transactionContext.setPropagatedBy(propagationText); } TransactionRequestImpl req = new TransactionRequestImpl(); req.setTransactionContext(transactionContext); req.setTargetTransactionCoordinator(beanRegistry.getConsumeCoordinator(propagationText)); transactionInterceptor.afterReceiveRequest(req); TransactionManager transactionManager = beanFactory.getTransactionManager(); Transaction transaction = transactionManager.getTransactionQuietly(); byte[] responseByteArray = SerializeUtils.serializeObject(transaction.getTransactionContext()); String responseTransactionStr = Base64.getEncoder().encodeToString(responseByteArray); response.setHeader(HEADER_TRANCACTION_KEY, responseTransactionStr); response.setHeader(HEADER_PROPAGATION_KEY, this.identifier); return true; } public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { if (HandlerMethod.class.isInstance(handler) == false) { return; } HandlerMethod hm = (HandlerMethod) handler; Class clazz = hm.getBeanType(); if (TransactionCoordinatorController.class.equals(clazz)) { return; } else if (ErrorController.class.isInstance(hm.getBean())) { return; } String transactionText = request.getHeader(HEADER_TRANCACTION_KEY); if (StringUtils.isBlank(transactionText)) { return; } SpringCloudBeanRegistry beanRegistry = SpringCloudBeanRegistry.getInstance(); TransactionBeanFactory beanFactory = beanRegistry.getBeanFactory(); TransactionManager transactionManager = beanFactory.getTransactionManager(); TransactionInterceptor transactionInterceptor = beanFactory.getTransactionInterceptor(); Transaction transaction = transactionManager.getTransactionQuietly(); TransactionContext transactionContext = transaction.getTransactionContext(); // byte[] byteArray = SerializeUtils.serializeObject(transactionContext); // String transactionStr = ByteUtils.byteArrayToString(byteArray); // response.setHeader(HEADER_TRANCACTION_KEY, transactionStr); // response.setHeader(HEADER_PROPAGATION_KEY, this.identifier); TransactionResponseImpl resp = new TransactionResponseImpl(); resp.setTransactionContext(transactionContext); resp.setSourceTransactionCoordinator(beanRegistry.getConsumeCoordinator(null)); transactionInterceptor.beforeSendResponse(resp); } public String getEndpoint() { return this.identifier; } public void setEndpoint(String identifier) { this.identifier = identifier; } public ApplicationContext getApplicationContext() { return applicationContext; } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } } ================================================ FILE: bytejta-supports-springcloud/src/main/java/org/bytesoft/bytejta/supports/springcloud/web/TransactionRequestInterceptor.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.springcloud.web; import java.io.IOException; import java.util.ArrayList; import java.util.Base64; import java.util.List; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.bytesoft.bytejta.TransactionImpl; import org.bytesoft.bytejta.supports.rpc.TransactionRequestImpl; import org.bytesoft.bytejta.supports.rpc.TransactionResponseImpl; import org.bytesoft.bytejta.supports.springcloud.SpringCloudBeanRegistry; import org.bytesoft.bytejta.supports.springcloud.loadbalancer.TransactionLoadBalancerInterceptor; import org.bytesoft.common.utils.CommonUtils; import org.bytesoft.common.utils.SerializeUtils; import org.bytesoft.transaction.TransactionBeanFactory; import org.bytesoft.transaction.TransactionContext; import org.bytesoft.transaction.TransactionManager; import org.bytesoft.transaction.archive.XAResourceArchive; import org.bytesoft.transaction.aware.TransactionEndpointAware; import org.bytesoft.transaction.remote.RemoteCoordinator; import org.bytesoft.transaction.remote.RemoteSvc; import org.bytesoft.transaction.supports.rpc.TransactionInterceptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpRequest; import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; import org.springframework.web.client.HttpClientErrorException; import com.netflix.appinfo.InstanceInfo; import com.netflix.loadbalancer.Server; import com.netflix.loadbalancer.Server.MetaInfo; import com.netflix.niws.loadbalancer.DiscoveryEnabledServer; public class TransactionRequestInterceptor implements ClientHttpRequestInterceptor, TransactionEndpointAware, ApplicationContextAware { static final Logger logger = LoggerFactory.getLogger(TransactionRequestInterceptor.class); static final String HEADER_TRANCACTION_KEY = "X-BYTEJTA-TRANSACTION"; // org.bytesoft.bytejta.transaction static final String HEADER_PROPAGATION_KEY = "X-BYTEJTA-PROPAGATION"; // org.bytesoft.bytejta.propagation static final String PREFIX_TRANSACTION_KEY = "/org/bytesoft/bytejta"; private String identifier; private ApplicationContext applicationContext; public ClientHttpResponse intercept(final HttpRequest httpRequest, byte[] body, ClientHttpRequestExecution execution) throws IOException { SpringCloudBeanRegistry beanRegistry = SpringCloudBeanRegistry.getInstance(); TransactionBeanFactory beanFactory = beanRegistry.getBeanFactory(); TransactionManager transactionManager = beanFactory.getTransactionManager(); TransactionImpl transaction = // (TransactionImpl) transactionManager.getTransactionQuietly(); String path = httpRequest.getURI().getPath(); int position = path.startsWith("/") ? path.indexOf("/", 1) : -1; String pathWithoutContextPath = position > 0 ? path.substring(position) : null; if (StringUtils.startsWith(path, PREFIX_TRANSACTION_KEY) // || StringUtils.startsWith(pathWithoutContextPath, PREFIX_TRANSACTION_KEY)) { return execution.execute(httpRequest, body); } else if (transaction == null) { return execution.execute(httpRequest, body); } final Map participants = transaction.getRemoteParticipantMap(); beanRegistry.setLoadBalancerInterceptor(new TransactionLoadBalancerInterceptor() { public List beforeCompletion(List servers) { final List readyServerList = new ArrayList(); final List unReadyServerList = new ArrayList(); for (int i = 0; servers != null && i < servers.size(); i++) { Server server = servers.get(i); // String instanceId = metaInfo.getInstanceId(); String instanceId = null; if (DiscoveryEnabledServer.class.isInstance(server)) { DiscoveryEnabledServer discoveryEnabledServer = (DiscoveryEnabledServer) server; InstanceInfo instanceInfo = discoveryEnabledServer.getInstanceInfo(); String addr = instanceInfo.getIPAddr(); String appName = instanceInfo.getAppName(); int port = instanceInfo.getPort(); instanceId = String.format("%s:%s:%s", addr, appName, port); } else { MetaInfo metaInfo = server.getMetaInfo(); String host = server.getHost(); String addr = host.matches("\\d+(\\.\\d+){3}") ? host : CommonUtils.getInetAddress(host); String appName = metaInfo.getAppName(); int port = server.getPort(); instanceId = String.format("%s:%s:%s", addr, appName, port); } if (participants.containsKey(instanceId)) { List serverList = new ArrayList(); serverList.add(server); return serverList; } // end-if (participants.containsKey(instanceId)) if (server.isReadyToServe()) { readyServerList.add(server); } else { unReadyServerList.add(server); } } logger.warn("There is no suitable server: expect= {}, actual= {}!", participants.keySet(), servers); return readyServerList.isEmpty() ? unReadyServerList : readyServerList; } public void afterCompletion(Server server) { if (server == null) { logger.warn( "There is no suitable server, the TransactionInterceptor.beforeSendRequest() operation is not executed!"); return; } else { try { // String instanceId = metaInfo.getInstanceId(); String instanceId = null; if (DiscoveryEnabledServer.class.isInstance(server)) { DiscoveryEnabledServer discoveryEnabledServer = (DiscoveryEnabledServer) server; InstanceInfo instanceInfo = discoveryEnabledServer.getInstanceInfo(); String addr = instanceInfo.getIPAddr(); String appName = instanceInfo.getAppName(); int port = instanceInfo.getPort(); instanceId = String.format("%s:%s:%s", addr, appName, port); } else { MetaInfo metaInfo = server.getMetaInfo(); String host = server.getHost(); String addr = host.matches("\\d+(\\.\\d+){3}") ? host : CommonUtils.getInetAddress(host); String appName = metaInfo.getAppName(); int port = server.getPort(); instanceId = String.format("%s:%s:%s", addr, appName, port); } invokeBeforeSendRequest(httpRequest, instanceId); } catch (IOException ex) { throw new RuntimeException(ex); } } } }); ClientHttpResponse httpResponse = null; boolean serverFlag = true; try { httpResponse = execution.execute(httpRequest, body); return httpResponse; } catch (HttpClientErrorException clientEx) { serverFlag = false; throw clientEx; } finally { beanRegistry.removeLoadBalancerInterceptor(); if (httpResponse != null) { this.invokeAfterRecvResponse(httpResponse, serverFlag); } // end-if (httpResponse != null) } } private void invokeBeforeSendRequest(HttpRequest httpRequest, String identifier) throws IOException { SpringCloudBeanRegistry beanRegistry = SpringCloudBeanRegistry.getInstance(); TransactionBeanFactory beanFactory = beanRegistry.getBeanFactory(); TransactionManager transactionManager = beanFactory.getTransactionManager(); TransactionInterceptor transactionInterceptor = beanFactory.getTransactionInterceptor(); TransactionImpl transaction = // (TransactionImpl) transactionManager.getTransactionQuietly(); TransactionContext transactionContext = transaction.getTransactionContext(); byte[] reqByteArray = SerializeUtils.serializeObject(transactionContext); String reqTransactionStr = Base64.getEncoder().encodeToString(reqByteArray); HttpHeaders reqHeaders = httpRequest.getHeaders(); reqHeaders.add(HEADER_TRANCACTION_KEY, reqTransactionStr); reqHeaders.add(HEADER_PROPAGATION_KEY, this.identifier); TransactionRequestImpl request = new TransactionRequestImpl(); request.setTransactionContext(transactionContext); RemoteCoordinator coordinator = beanRegistry.getConsumeCoordinator(identifier); request.setTargetTransactionCoordinator(coordinator); transactionInterceptor.beforeSendRequest(request); } private void invokeAfterRecvResponse(ClientHttpResponse httpResponse, boolean serverFlag) throws IOException { SpringCloudBeanRegistry beanRegistry = SpringCloudBeanRegistry.getInstance(); TransactionBeanFactory beanFactory = beanRegistry.getBeanFactory(); TransactionInterceptor transactionInterceptor = beanFactory.getTransactionInterceptor(); HttpHeaders respHeaders = httpResponse.getHeaders(); String respTransactionStr = respHeaders.getFirst(HEADER_TRANCACTION_KEY); String respPropagationStr = respHeaders.getFirst(HEADER_PROPAGATION_KEY); String transactionText = StringUtils.trimToNull(respTransactionStr); byte[] byteArray = StringUtils.isBlank(transactionText) ? null : Base64.getDecoder().decode(transactionText); TransactionContext serverContext = byteArray == null || byteArray.length == 0 // ? null : (TransactionContext) SerializeUtils.deserializeObject(byteArray); TransactionResponseImpl txResp = new TransactionResponseImpl(); txResp.setTransactionContext(serverContext); RemoteCoordinator serverCoordinator = beanRegistry.getConsumeCoordinator(respPropagationStr); txResp.setSourceTransactionCoordinator(serverCoordinator); txResp.setParticipantDelistFlag(serverFlag ? false : true); transactionInterceptor.afterReceiveResponse(txResp); } public ApplicationContext getApplicationContext() { return applicationContext; } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } public String getEndpoint() { return this.identifier; } public void setEndpoint(String identifier) { this.identifier = identifier; } } ================================================ FILE: bytejta-supports-springcloud/src/main/resources/bytejta-supports-springcloud.xml ================================================ ================================================ FILE: pom.xml ================================================ 4.0.0 org.bytesoft bytejta-parent 0.5.0-BETA9 pom bytejta-parent ByteJTA is a XA-complicant transaction manager. http://www.bytesoft.org The GNU Lesser General Public License, Version 3.0 http://www.gnu.org/licenses/lgpl-3.0.txt liuyangming bytefox@126.com scm:git:git@github.com:liuyangming/ByteJTA.git scm:git:git@github.com:liuyangming/ByteJTA.git git@github.com:liuyangming/ByteJTA.git Github Issue https://github.com/liuyangming/ByteJTA/issues release org.apache.maven.plugins maven-source-plugin 2.2.1 package jar-no-fork org.apache.maven.plugins maven-javadoc-plugin 2.9.1 package jar org.apache.maven.plugins maven-gpg-plugin 1.5 verify sign oss https://oss.sonatype.org/content/repositories/snapshots/ oss https://oss.sonatype.org/service/local/staging/deploy/maven2/ UTF-8 javax.transaction javax.transaction-api 1.2 javax.jms javax.jms-api 2.0 javax.resource javax.resource-api 1.7 javax.servlet javax.servlet-api 3.1.0 javax.inject javax.inject 1 javax.annotation javax.annotation-api 1.2 org.springframework spring-context 5.0.8.RELEASE org.springframework spring-tx 5.0.8.RELEASE org.springframework spring-jdbc 5.0.8.RELEASE org.springframework spring-aop 5.0.8.RELEASE org.springframework spring-webmvc 5.1.0.RELEASE org.springframework.retry spring-retry 1.2.2.RELEASE org.springframework.boot spring-boot 2.0.4.RELEASE org.springframework.boot spring-boot-autoconfigure 2.0.4.RELEASE org.springframework.boot spring-boot-starter-web 2.0.4.RELEASE org.springframework.cloud spring-cloud-starter-netflix-eureka-server 2.0.2.RELEASE org.springframework.cloud spring-cloud-netflix-eureka-client 2.0.1.RELEASE org.springframework.cloud spring-cloud-starter-netflix-ribbon 2.0.2.RELEASE org.springframework.cloud spring-cloud-starter-openfeign 2.0.2.RELEASE org.springframework.cloud spring-cloud-starter-netflix-hystrix 2.0.2.RELEASE org.springframework.cloud spring-cloud-netflix-core 2.0.1.RELEASE org.springframework.cloud spring-cloud-openfeign-core 2.0.1.RELEASE org.springframework.cloud spring-cloud-commons 2.0.1.RELEASE org.springframework.cloud spring-cloud-config-client 2.0.1.RELEASE org.springframework.cloud spring-cloud-zookeeper-core 2.0.0.RELEASE org.bytesoft bytejta-core 0.5.0-BETA9 org.bytesoft bytejta-supports 0.5.0-BETA9 org.bytesoft bytejta-supports-springcloud 0.5.0-BETA9 org.bytesoft bytejta-supports-dubbo 0.5.0-BETA9 org.slf4j slf4j-api 1.7.6 com.alibaba dubbo 2.6.9 org.apache.commons commons-lang3 3.4 commons-io commons-io 2.4 org.apache.commons commons-dbcp2 2.1.1 com.fasterxml.jackson.core jackson-core 2.9.9 com.fasterxml.jackson.core jackson-databind 2.9.10.8 com.fasterxml.jackson.core jackson-annotations 2.9.9 io.netty netty-buffer 4.1.9.Final io.netty netty-codec 4.1.9.Final io.netty netty-codec-http 4.1.71.Final io.netty netty-common 4.1.9.Final io.netty netty-handler 4.1.45.Final io.netty netty-transport 4.1.9.Final io.netty netty-transport-native-epoll 4.1.9.Final com.caucho hessian 4.0.38 aopalliance aopalliance 1.0 cglib cglib 2.2.2 org.ow2.asm asm 5.0.4 org.aspectj aspectjweaver 1.8.13 org.apache.curator curator-framework 4.0.1 org.apache.curator curator-client 4.0.1 org.apache.zookeeper zookeeper org.apache.curator curator-recipes 4.0.1 io.netty netty org.mongodb mongo-java-driver 3.8.1 org.apache.zookeeper zookeeper 3.4.14 com.esotericsoftware kryo 3.0.3 org.reactivestreams reactive-streams 1.0.2 io.projectreactor reactor-core 3.1.9.RELEASE io.reactivex rxjava-reactive-streams 1.2.1 io.reactivex.rxjava2 rxjava 2.2.2 com.squareup.okhttp3 okhttp 3.11.0 org.hibernate hibernate-core 5.4.0.Final org.springframework.data spring-data-jpa 2.1.8.RELEASE org.hibernate hibernate-entitymanager 5.4.0.Final org.springframework.boot spring-boot-starter-data-jpa 2.1.1.RELEASE maven-compiler-plugin 2.3.2 1.8 1.8 UTF-8 org.codehaus.plexus plexus-compiler-javac 1.8.1 spring-milestones Spring Milestones https://repo.spring.io/libs-milestone false bytejta-core bytejta-supports bytejta-supports-springcloud bytejta-supports-dubbo