Jetspeed Transactions Configuration

All Jetspeed components that use the database are transactional. This guide helps you learn about how Jetspeed components are wired together in the context of Spring transactions. When deploying Jetspeed, you may need to modify some of the componnents that are transactional. This guide will help you understand how to configure transactional beans (services) in Jetspeed.

Transactions in Spring

Transactions allow you to group several operations into a single unit of work that either fully happens or fully doesn't happen. Jetspeed leverages the Spring Framework to make its components transactional. The Spring Framework provides the transaction management required to execute transactions across two or more Jetspeed components. Jetspeed transactions are configured as declarative transactions. Jetspeed currently does not make use of programmatic transactions. The Spring Framework provides the declarative transaction management through Spring's AOP framework. Spring provides three ways to declare transactional boundaries in the spring configuration.

  1. Spring Transactional Interceptors, Bean Wrapping
  2. XML declared transactions
  3. Annotations
Jetspeed uses the original Spring transactional support (1): wrapping Jetspeed components with transactional interceptors. When configuring beans in Java, be aware that any bean that uses a database is going to be wrapped with a transactional wrapper. The transaction advice is handled in the transaction interceptor. When injecting dependencies, the transaction interceptor, not the actual Jetspeed service implementation, is injected as a dependency into other Jetpeed services.

Declarative Transactions via Interception

Lets take one example of a transaction Jetspeed service: the Permission Manager service. This service is configured in the security-managers.xml file. The first bean is actually named with a Impl suffix. This is because this is the implementation bean: the bean name org.apache.jetspeed.security.impl.PermissionManagerImpl is the same name as the class. Class attributes represent the class name of the implementing class.

The second bean is named by interface: org.apache.jetspeed.security.PermissionManager. This bean is not the actual Permission implementation. The bean definition is a transactional proxy: it is an bean wrapper or interceptor. When wiring the Permission Manager to another Jetspeed service, make sure to use the proxy object, not the actual implementation. This ensures that declarative transaction support in Spring can intercept calls to the Permission Manager.

Notice that the parent bean is baseTransactionProxy. This is how we get the transactional support: through Spring AOP inheriting the base functionality for proxying transactions declaratively.

  <!-- Security: Permission Manager -->
  <bean id="org.apache.jetspeed.security.impl.PermissionManagerImpl" 
  	   class="org.apache.jetspeed.security.impl.PermissionManagerImpl"  />
  
  <bean id="org.apache.jetspeed.security.PermissionManager" parent="baseTransactionProxy" 
		name="permissionManager" >
		<property name="proxyInterfaces">
			<value>org.apache.jetspeed.security.PermissionManager</value>
		</property>
		<property name="target">
			<ref bean="org.apache.jetspeed.security.impl.PermissionManagerImpl"/>
		</property>
		<property name="transactionAttributes">
			<props>				
				<prop key="remove*">PROPAGATION_REQUIRED</prop>
				<prop key="grant*">PROPAGATION_REQUIRED</prop>
				<prop key="revoke*">PROPAGATION_REQUIRED</prop>
				<prop key="grant*">PROPAGATION_REQUIRED</prop>
				<prop key="add*">PROPAGATION_REQUIRED</prop>
				<prop key="update*">PROPAGATION_REQUIRED</prop>
				<prop key="*">PROPAGATION_SUPPORTS</prop>
			</props>
		</property>
   </bean>

There are several transactional attributes that control the behavior of transactions on the wrappered bean

  • proxyInterfaces - the name of the interface to automatically proxy calls to. Only calls to this interface will be intercepted. This ensures that Jetspeed services only interact with each other along the interface contract of the Jetspeed API.
  • target - the name of the bean being proxied for transactions. In this case its our Permission Manager implementation
  • transactionalAttributes - attributes that can control the behavior of methods on the proxied interface. Methods are listed as property keys and can contain wildcards such as all methods that start with add*. Then the body of the tag can contain either PROPAGATION_REQUIRED or PROPAGATION_SUPPORTS keywords, as well as exception handling behavior described below.

Transaction Propagation Required - If propagation is required, then the method is required to be a part of a transaction. If, when the method is called, there is not an active transaction, a transaction will be immediately started by the proxy (interceptor). Methods that store to a persistent database are usually marked as propagation required.

Transaction Propagation Supported - If propagation is only supported, then the method is NOT required to be a part of a transaction. If, when the method is called, there is not an active transaction, a transaction will be NOT be started by the proxy (interceptor). If there is a transaction active, the method will join the transaction. Methods not marked with either required or supported properties will not participate in transactions.

Exception Handling and Rollback - Transactions can also be committed or rolled back based on one or more Exception classes. In the example below we use -, the minus sign, to denote that any RegistrationExceptions that occur on any methods that start with register* will cause the transaction to rollback. On the contrary, using a +, a plus sign, will cause the transaction to commit for the given method and exception.

				
        <property name="transactionAttributes">
            <props>
                <prop key="register*">PROPAGATION_REQUIRED,-org.apache.jetspeed.administration.RegistrationException</prop>
            </props>
        </property>

Nesting and Joining Transactions

Transactions can be nested when one service calls another service. Take for example, the User Registration portlet which uses the Jetspeed Administration service method named registerUser. User registration requires transactional calls to both the Permission Manager and the User Manager. The transaction begins on the Jetspeed Administration service, calls into the Permission Manager to the grantPermission method, and then the User Manager's storeUser method. If any of these methods fails, the entire transaction is rolled back. If all methods succeed, the transaction is committed as shown below.