Spring FrameworkのトランザクションをLibertyでためす

# 目的

LibertyのSpring Framework 環境を構築して HelloWorld のような簡単なプログラムでテストします。

# Spring プログラムの説明

Eclipse で必要な ソースコードとXMLを用意するところからの説明です。

## SomeBean.java

ここで 
- transactionManager は bean の property で application-context.xml に指定
    - https://spring.pleiades.io/spring-framework/reference/core/beans/dependencies/factory-properties-detailed.html
- @Transactional は <tx:annotation-driven/>
    - https://spring.pleiades.io/spring-framework/reference/data-access/transaction/declarative/annotations.html
- test は transactionManager.getTransaction でトランザクション開始

```SomeBean.java
package pdprof.spring.transaction;

import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.jta.WebSphereUowTransactionManager;
import org.springframework.transaction.support.DefaultTransactionDefinition;

public class SomeBean {

	WebSphereUowTransactionManager transactionManager;

	public WebSphereUowTransactionManager getTransactionManager() {
		return transactionManager;
	}

	public void setTransactionManager(WebSphereUowTransactionManager transactionManager) {
		this.transactionManager = transactionManager;
	}

	@Transactional
	public void testAnnotation() {
		System.out.println("Some testAnnotation()");
	}
	
	public void test() {

		try {
			DefaultTransactionDefinition def = new DefaultTransactionDefinition();
			def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
			TransactionStatus status = transactionManager.getTransaction(def);
			System.out.println("Is the transaction new ? " + status.isNewTransaction());
			transactionManager.commit(status);

		} catch (TransactionException e) {
			e.printStackTrace();
		}

		System.out.println("Some test()");
	}
}
```


## application-context.xml

- SomeBean.java から使用される内容を指定する

```application-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
        http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.8.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsd">
	
	<tx:jta-transaction-manager/>
	<tx:annotation-driven/>
	<bean id="transactionManager"
		class="org.springframework.transaction.jta.WebSphereUowTransactionManager">
	</bean>

	<bean id="someBean" class="pdprof.spring.transaction.SomeBean">
		<property name="transactionManager">
			<ref bean="transactionManager" />
		</property>
	</bean>
</beans>
```

## ApplicationContext の初期化

- ApplicationContext を application-context.xml を読み込んで初期化
- サーブレット実行時に test(), testAnnotation() メソッドを実行してトランザクションをテスト

```SpringTransactionServlet.java
package pdprof.spring.transaction;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Servlet implementation class SpringTransactionServlet
 */
@WebServlet("/transaction")
public class SpringTransactionServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	/**
	 * Default constructor.
	 */
	public SpringTransactionServlet() {
		// TODO Auto-generated constructor stub
	}

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		test();
		response.getWriter().append("Served at: ").append(request.getContextPath());
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doGet(request, response);
	}

	public void test() {

		ApplicationContext context = new ClassPathXmlApplicationContext("/META-INF/application-context.xml");
		SomeBean bean = (SomeBean) context.getBean("someBean");
		bean.test();
		bean.testAnnotation();

	}

}
```
# Libertyテスト環境

これまでの方法で既存環境に構成は可能ですが、環境をdockerで用意する方法です。

今回は tWAS でも動作するように、Java8, jsp-2.3, jpa-2.2 で動かします。
server 構成は以下になります。

```server.xml
<server description="new server">

    <!-- Enable features -->
    <featureManager>
        <feature>jsp-2.3</feature>
        <feature>jdbc-4.2</feature>
        <feature>cdi-2.0</feature>
        <feature>jpa-2.2</feature>
        <feature>localConnector-1.0</feature>
        <feature>eventLogging-1.0</feature>
        <feature>requestTiming-1.0</feature>
    </featureManager>

    <httpAccessLogging id="pdprofAccessLogging" logFormat="%h %u %t &quot;%r&quot; %s %b %{remote}p %D %{JSESSIONID}C %{Set-Cookie}o"/>
    <httpEndpoint accessLoggingRef="pdprofAccessLogging" host="*" httpPort="9080" httpsPort="9443" id="defaultHttpEndpoint"/>

    <eventLogging logMode="entryExit" minDuration="1ms"/>

    <requestTiming slowRequestThreshold="5s" hungRequestThreshold="10m">
    </requestTiming>

    <logging  traceSpecification="TransactionSummary=all"
      traceFileName="trace.log"
      maxFileSize="20"
      maxFiles="10"
      traceFormat="BASIC" />

   <basicRegistry id="basic">
      <user name="wsadmin" password="passw0rd" />
      <user name="reader" password="passw0rd" />
   </basicRegistry>
   <administrator-role>
      <user>wsadmin</user>
   </administrator-role>
   <reader-role>
      <user>reader</user>
   </reader-role>
    <!-- Automatically expand WAR files and EAR files -->
    <applicationManager autoExpand="true"/>

    <!-- Default SSL configuration enables trust for default certificates from the Java runtime --> 
    <ssl id="defaultSSLConfig" trustDefaultCerts="true"/>

    <applicationMonitor updateTrigger="mbean"/>

    <webApplication id="spring.transaction" location="spring.transaction.war" name="spring.transaction"/>
</server>
```

# Libertyテスト環境の自動構築


これまでの方法で既存環境に構成は可能ですが、環境をdockerで用意する方法です。

用意したGitレポジトリをクローンします。
```
git clone http://github.com/pdprof/spring-transaction.git
cd spring-transaction/spring-transaction-docker
```

Libertyのイメージをビルドして開始します。
```
./setup-docker.sh
./start.sh
```

Libertyの起動を確認します。SpringTransactionServlet にアクセスします。

http://localhost:9080/spring.transaction/transaction

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/262366/2c0833bf-b7b6-4453-85f9-e2ecb7ec11a9.png)

と表示されれば問題ありません。


# トランザクション処理を確認

docker container にアクセスしてtrace.logでトランザクションを確認します。

```bash
podman exec -it spring-transaction bash
more /logs/trace.log
```


getTransaction() でトランザクション開始

org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:373)

の呼び出しが確認できます。

```console
4/29/25 15:01:52:648 UTC] 00000033 TransactionIm 3   Transaction created. 
                                 com.ibm.tx.jta.embeddable.impl.EmbeddableTransactionImpl@a8adf6fe#tid=138827292,active=0,suspended=0,Not on a thread,gtrid=0000019682119585000000010846561c88a9e4b2aacda5c60ff4e6ce705569cfddc6e538
                                 {XidImpl: formatId(57415344), gtrid_length(36), bqual_length(40),
data(0000019682119585000000010846561c88a9e4b2aacda5c60ff4e6ce705569cfddc6e5380000019682119585000000010846561c88a9e4b2aacda5c60ff4e6ce705569cfddc6e53800000001)}
 trace                           java.lang.Throwable: Transaction creation stack--More--(41%)
	at com.ibm.tx.jta.impl.TransactionImpl.traceCreate(TransactionImpl.java:3383)
	at com.ibm.tx.jta.impl.TransactionImpl.<init>(TransactionImpl.java:334)
	at com.ibm.tx.jta.embeddable.impl.EmbeddableTransactionImpl.<init>(EmbeddableTransactionImpl.java:89)
	at com.ibm.tx.jta.embeddable.impl.EmbeddableTranManagerImpl.createNewTransaction(EmbeddableTranManagerImpl.java:78)
	at com.ibm.tx.jta.embeddable.impl.EmbeddableTranManagerImpl.createNewTransaction(EmbeddableTranManagerImpl.java:30)
	at com.ibm.tx.jta.impl.TranManagerImpl.beginUserTran(TranManagerImpl.java:124)
	at com.ibm.tx.jta.impl.TranManagerSet.beginUserTran(TranManagerSet.java:107)
	at com.ibm.tx.jta.impl.UserTransactionImpl.begin(UserTransactionImpl.java:66)
	at com.ibm.tx.jta.embeddable.impl.EmbeddableUserTransactionImpl.begin(EmbeddableUserTransactionImpl.java:71)
	at com.ibm.ws.transaction.services.UserTransactionService.begin(UserTransactionService.java:68)
	at org.springframework.transaction.jta.JtaTransactionManager.doJtaBegin(JtaTransactionManager.java:886)
TransactionManager.java:849)rk.transaction.jta.JtaTransactionManager.doBegin(Jta--More--(48%)
	at org.springframework.transaction.support.AbstractPlatformTransactionManager.startTransaction(AbstractPlatformTransactionManager.java:400)
	at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:373)
	at pdprof.spring.transaction.SomeBean.test(SomeBean.java:32)
	at pdprof.spring.transaction.SomeBean$$FastClassBySpringCGLIB$$b1dba14.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
	at org.springframework.aop.framework.CglibAopProxy.invokeMethod(CglibAopProxy.java:386)
	at org.springframework.aop.framework.CglibAopProxy.access$000(CglibAopProxy.java:85)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:703)
	at pdprof.spring.transaction.SomeBean$$EnhancerBySpringCGLIB$$bd4c84c6.test(<generated>)
	at pdprof.spring.transaction.SpringTransactionServlet.test(SpringTransactionServlet.java:50)
	at pdprof.spring.transaction.SpringTransactionServlet.doGet(SpringTransactionServlet.java:33)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:686)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:791)

```

@Transactional でのトランザクション開始

`<tx:annotation-driven/>` と `<bean id="transactionManager" ../>` の指定で

org.springframework.transaction.jta.WebSphereUowTransactionManager.execute(WebSphereUowTransactionManager.java:306)

が実行されています。トレースが出力されていることでLibertyのトランザクションマネージャーが使われていることが分かります。

```console
[4/29/25 15:01:52:660 UTC] 00000033 TransactionIm 3   Transaction created. 
                                 com.ibm.tx.jta.embeddable.impl.EmbeddableTransactionImpl@fd6d3e5b#tid=138827300,active=0,suspended=0,Not on a thread,gtrid=0000019682119594000000010846562488a9e4b2aacda5c60ff4e6ce705569cfddc6e538
                                 {XidImpl: formatId(57415344), gtrid_length(36), bqual_length(40),
data(0000019682119594000000010846562488a9e4b2aacda5c60ff4e6ce705569cfddc6e5380000019682119594000000010846562488a9e4b2aacda5c60ff4e6ce705569cfddc6e53800000001)}
                                 java.lang.Throwable: Transaction creation stack trace
	at com.ibm.tx.jta.impl.TransactionImpl.traceCreate(TransactionImpl.java:3383)
	at com.ibm.tx.jta.impl.TransactionImpl.<init>(TransactionImpl.java:334)
	at com.ibm.tx.jta.embeddable.impl.EmbeddableTransactionImpl.<init>(EmbeddableTransactionImpl.java:89)
nsaction(EmbeddableTranManagerImpl.java:78)mbeddableTranManagerImpl.createNewTra--More--(74%)
	at com.ibm.tx.jta.embeddable.impl.EmbeddableTranManagerImpl.createNewTransaction(EmbeddableTranManagerImpl.java:30)
	at com.ibm.tx.jta.impl.TranManagerImpl.beginUserTran(TranManagerImpl.java:124)
	at com.ibm.tx.jta.impl.TranManagerSet.beginUserTran(TranManagerSet.java:107)
	at com.ibm.tx.jta.impl.UserTransactionImpl.begin(UserTransactionImpl.java:66)
	at com.ibm.ws.uow.embeddable.EmbeddableUOWManagerImpl.uowBegin(EmbeddableUOWManagerImpl.java:885)
	at com.ibm.ws.uow.embeddable.EmbeddableUOWManagerImpl.runUnderNewUOW(EmbeddableUOWManagerImpl.java:727)
	at com.ibm.ws.uow.embeddable.EmbeddableUOWManagerImpl.runUnderUOW(EmbeddableUOWManagerImpl.java:331)
	at org.springframework.transaction.jta.WebSphereUowTransactionManager.execute(WebSphereUowTransactionManager.java:306)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:417)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:762)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:707)
	at pdprof.spring.transaction.SomeBean$$EnhancerBySpringCGLIB$$bd4c84c6.testAnnotation(<generated>)
	at pdprof.spring.transaction.SpringTransactionServlet.test(SpringTransactionServlet.java:51)
	at pdprof.spring.transaction.SpringTransactionServlet.doGet(SpringTransactionServlet.java:33)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:686)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:791)


```

# トラブルシューティング

毎度のことですが、よくつかう dockerコマンドを書いておきます。この文書でもpodman, dockerとまとまりがないですが、Linux では dockerがpodmanを呼び出すようになっていました。

```
docker ps # 動作中のContainer確認
docker logs -f spring-transaction # ログ確認
docker restart spring-transaction # 再起動
docker inspect spring-transaction # container 確認
docker exec -it spring-transaction bash # Container内状況確認
```

# まとめ

Libertyの環境を構築して、Spring Framework のトランザクションをテストしました。

# 参考

以下ページを参考にさせていただきました。

https://docs.spring.io/spring-framework/reference/