本文是针对《使用Jencks实现Hibernate与Jackrabbit的分布式事务》(以下简称《JHJ》)一文的理论分析。若只关心实现,请忽略此文点击这里跳转到实现的参考示例。
本文先简单介绍JTA和JCA的一些概念作为理论基础,之后再分析Jencks如何实现Hibernate与Jackrabbit的分布式事务的。
JTA全称为Java Transaction API,顾名思义JTA定义了一组统一的事务编程的接口,这些接口如下:
XAResource
XAResource接口是对实现了X/Open CAE规范的资源管理器 (Resource Manager,数据库就是典型的资源管理器) 的抽象,它由资源适配器 (Resource Apdater) 提供实现。XAResource是支持事务控制的核心。
Transaction接口是一个事务实例的抽象,通过它可以控制事务内多个资源的提交或者回滚。二阶段提交过程也是由Transaction接口的实现者来完成的。
托管模式 (managed mode) 下,TransactionManager接口是被应用服务器调用,以控制事务的边界的。
非托管模式 (non-managed mode) 下,应用程序可以通过UserTransaction接口控制事务的边界
更多细节请参见:
有关JCA
上图为JCA的架构图,中间涉及元素说明如下:
简称EIS,在JTA中它又被称为资源管理器。典型的EIS有数据库,事务处理系统(Transaction Processing System),ERP系统。
资源适配器(Resource Adaper)是JCA的关键。要想把不同的EIS整合(或者连接)到J2EE运行环境中,就必须为每个EIS提供资源适配器,它会将将EIS适配为一个具备统一编程接口的资源 (Resource) 。这个统一编程接口就是上图中的System Contracts和Client API。下面的UML类图将完美诠释资源适配器。
应用服务器 (Application Server) 通过System Contracts来管理对EIS的安全、事务、连接等。典型的应用服务器有JBoss、JOnAS、Geronimo、GlassFish等。
应用组件 (Application Component) ,它封装了应用业务逻辑,像对资源的访问和修改。典型的应用组件就是EJB。
更多细节请参见:
回到用《JHJ》的问题上来,上面关于JTA与JCA到底能够提供哪些帮助呢?总结一下有两点:
众所周知,应用服务器是提供事务管理器实现的,但这不意味着我们只能选择应用服务器,不然就没有必要写《JHJ》和此文了。这里我选择了Jencks,它是一个轻量级的JCA容器,能够很容易与Spring进行集成,并由Spring的JtaTransactionManager将事务管理的职责委派给Jencks。
资源适配器
前面提到了资源适配器实现事务支持的关键——XAResource,但它并非直接暴露出来的,需要通过ManagedConnection接口获取,而ManageConnection又由ManageConnectionFactory接口来提供。因此,资源适配器的问题就落实到寻找ManageConnectionFactory接口实现的提供者。
Jackrabbit项目中有个组件叫jackrabbit-jca,提供了ManageConnectionFactory接口的实现类JCAManagedConnectionFactory。
数据库方面,Jencks为其提供了ManageConnectionFactory接口的实现类DataSourceMCF。
连接管理器
有了事务管理器和资源适配器还没有完,因为在 有关JTA 中有两个问题没有解决:
这两个问题由连接管理器解决,连接管理器负责管理ManageConnectionFactory,当应用获取连接时,连接管理器要做两件事:
下面结合《JHJ》示例来看连接管理器是如何做到的吧:
<!-- Jackrabbit --> <bean id="repository" class="org.springframework.jca.support.LocalConnectionFactoryBean"> <property name="managedConnectionFactory"> <ref local="repositoryManagedConnectionFactory" /> </property> <property name="connectionManager"> <bean parent="connectionManager" /> </property> </bean> <!-- Database --> <bean id="dataSource" class="org.jencks.factory.ConnectionFactoryFactoryBean"> <property name="managedConnectionFactory" ref="jdbcManagedConnectionFactory" /> <property name="connectionManager"> <bean parent="connectionManager" /> </property> </bean>
<!-- 链接管理器 --> <bean id="connectionManager" class="org.jencks.factory.ConnectionManagerFactoryBean" abstract="true"> <property name="transactionManager"> <ref local="delegateTransactionManager" /> </property> <property name="transaction" value="xa" /> </bean>
// DataSourceMCF.java public Object createConnectionFactory(ConnectionManager connectionManager) throws ResourceException { return new DataSource(this, connectionManager); } // JCAManagedConnectionFactory.java public Object createConnectionFactory(ConnectionManager cm) throws ResourceException { createRepository(); JCARepositoryHandle handle = new JCARepositoryHandle(this, cm); log("Created repository handle (" + handle + ")"); return handle; }
// DataSource.java public Connection getConnection() throws SQLException { try { return (Connection) cm.allocateConnection(mcf, containerRequestInfo); } catch (ResourceException e) { ... } } // JCARepositoryHandle.java private Session login(JCAConnectionRequestInfo cri) throws LoginException, NoSuchWorkspaceException, RepositoryException { try { return (Session) cm.allocateConnection(mcf, cri); } catch (ResourceException e) { ... } }
// GenericConnectionManager.java public Object allocateConnection(ManagedConnectionFactory managedConnectionFactory, ConnectionRequestInfo connectionRequestInfo) throws ResourceException { ManagedConnectionInfo mci = new ManagedConnectionInfo(managedConnectionFactory, connectionRequestInfo); ConnectionInfo ci = new ConnectionInfo(mci); getStack().getConnection(ci); // 这里通过拦截器机制完成事件监听注册和enlistReource Object connection = ci.getConnectionProxy(); if (connection == null) { connection = ci.getConnectionHandle(); } return connection; } // MCFConnectionInterceptor.java public void getConnection(ConnectionInfo connectionInfo) throws ResourceException { // ManagedConnectionInfo mci = ... try { // ManagedConnection mc = ... ... GeronimoConnectionEventListener listener = new GeronimoConnectionEventListener(stack, mci); mci.setConnectionEventListener(listener); mc.addConnectionEventListener(listener); } catch (ResourceException re) { ... } } // TransactionEnlistingInterceptor.java public void getConnection(ConnectionInfo connectionInfo) throws ResourceException { next.getConnection(connectionInfo); try { ManagedConnectionInfo mci = connectionInfo.getManagedConnectionInfo(); Transaction transaction = TxUtil.getTransactionIfActive(transactionManager); if (transaction != null) { XAResource xares = mci.getXAResource(); transaction.enlistResource(xares); } } catch (Exception e) { ... } }
解读《JHJ》的目的只为抛砖引玉,激发思考和探讨。此外,这里留下一个问题,在Spring的JtaTransactionManager的源码中发现,spring使用的是UserTransaction而不是TransactionManager这是为何呢?