これは、なにをしたくて書いたもの?
前に、Jakarta Data(Hibernate)をJava SE環境で動かしてみました。
Jakarta DataをJava SE環境で動かしてみる(Hibernate、Weld、Narayana、Agroal) - CLOVER🍀
今回はここにJakarta RESTful Web Services(JAX-RS)とJakarta Transactions(JTA)を組み合わせてみます。
つまりJava SE環境でこのあたりを強引に動かしてみようという試みですが、基本的にネタです。ネタなのですが、
今後やりたいことの足がかりにする予定もあります。
方針
こちらのエントリーで行った方法はほぼそのまま使います。
Jakarta DataをJava SE環境で動かしてみる(Hibernate、Weld、Narayana、Agroal) - CLOVER🍀
またこちらの知識も元になっています。
JAX-RS 3.1.0のSeBootstrapを使って、RESTEasy(+Undertow、CDI)をJava SE環境で動かす - CLOVER🍀
できるようにしたいことはこんな感じです。
- Java SE環境で、アプリケーションサーバーなしで実行する
- JAX-RS、CDI、JTA、Jakarta Dataを使う
- JTAは
@Transactionalを使って宣言的トランザクションができるようにする
つまり、アプリケーションコードをパッと見た感じではいかにもJakarta EE環境で動かしているような状態になっている
ことを目指します。
そこに実用性はありません。つまり、あまり説明する気もありません(笑)。ただ、この後ちょっと使いたい予定が
あるのです。
利用するライブラリーなどはこんな感じになります。
- JAX-RS … RESTEasy
- Servlet … Undertow
- CDI … Weld
- JTA … Narayana
- Jakarta Data … Hibernate
- JNDI … Simple-JNDI
- コネクションプール … Agroal
- クラスのインデックス … Jandex
完全にネタ枠ですが、書いていきましょう。
環境
今回の環境はこちら。
$ java --version openjdk 25.0.3 2026-04-21 OpenJDK Runtime Environment (build 25.0.3+9-2-24.04.2-Ubuntu) OpenJDK 64-Bit Server VM (build 25.0.3+9-2-24.04.2-Ubuntu, mixed mode, sharing) $ mvn --version Apache Maven 3.9.16 (2bdd9fddda4b155ebf8000e807eb73fd829a51d5) Maven home: $HOME/.sdkman/candidates/maven/current Java version: 25.0.3, vendor: Ubuntu, runtime: /usr/lib/jvm/java-25-openjdk-amd64 Default locale: ja_JP, platform encoding: UTF-8 OS name: "linux", version: "6.8.0-136-generic", arch: "amd64", family: "unix"
データベースはMySQLを使います。172.17.0.2で動作しているものとします。
MySQL localhost:33060+ ssl practice SQL > select version(); +-----------+ | version() | +-----------+ | 8.4.11 | +-----------+ 1 row in set (0.0004 sec)
MySQLは一般ログを有効にして、発行されたクエリーが確認できるようにしています。
general_log = ON
準備
Maven依存関係などはこちら。
<properties> <maven.compiler.release>25</maven.compiler.release> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>resteasy-bom</artifactId> <version>7.0.2.Final</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.hibernate.orm</groupId> <artifactId>hibernate-platform</artifactId> <version>7.4.5.Final</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>resteasy-undertow-cdi</artifactId> </dependency> <dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>resteasy-json-binding-provider</artifactId> </dependency> <dependency> <groupId>jakarta.data</groupId> <artifactId>jakarta.data-api</artifactId> </dependency> <dependency> <groupId>org.jboss.narayana.jta</groupId> <artifactId>cdi</artifactId> <version>7.3.4.Final</version> </dependency> <dependency> <groupId>org.hibernate.orm</groupId> <artifactId>hibernate-core</artifactId> </dependency> <dependency> <groupId>org.hibernate.orm</groupId> <artifactId>hibernate-scan-jandex</artifactId> </dependency> <dependency> <groupId>io.agroal</groupId> <artifactId>agroal-api</artifactId> <version>3.2.1</version> </dependency> <dependency> <groupId>io.agroal</groupId> <artifactId>agroal-pool</artifactId> <version>3.2.1</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.agroal</groupId> <artifactId>agroal-narayana</artifactId> <version>3.2.1</version> </dependency> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <version>9.7.0</version> <scope>runtime</scope> </dependency> <dependency> <groupId>com.github.h-thurow</groupId> <artifactId>simple-jndi</artifactId> <version>0.25.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>io.smallrye</groupId> <artifactId>jandex-maven-plugin</artifactId> <version>3.6.0</version> <executions> <execution> <id>make-index</id> <goals> <goal>jandex</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.15.0</version> <configuration> <annotationProcessorPaths> <path> <groupId>org.hibernate.orm</groupId> <artifactId>hibernate-processor</artifactId> </path> </annotationProcessorPaths> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>3.11.0</version> <executions> <execution> <id>copy-dependencies</id> <phase>prepare-package</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <outputDirectory>${project.build.directory}/libs</outputDirectory> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>3.5.1</version> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> <classpathPrefix>libs/</classpathPrefix> <mainClass>org.littlewings.jakarta.App</mainClass> </manifest> </archive> </configuration> </plugin> </plugins> </build>
Narayanaのcdiというアーティファクトがありますが、これは@Transactionalでトランザクション管理を行うために
必要です。
Maven Dependency PluginとMaven Jar Pluginが含まれているのは、java -jarコマンドで実行できるようにするためですね。
Jandex Maven Pluginは、JAX-RSリソースクラスを検出するために使います。
ソースコードを書く
では、基本的なソースコードを書いていきましょう。そのままだと動かないのですが、Jakarta EE環境であれば動作する
コードや設定ファイルです。
エンティティークラス。
src/main/java/org/littlewings/jakarta/book/Book.java
package org.littlewings.jakarta.book; import jakarta.persistence.Entity; import jakarta.persistence.Id; import jakarta.persistence.Table; @Entity @Table(name = "book") public class Book { @Id private String isbn; private String title; private Integer price; // getter/setterは省略 }
リポジトリーインターフェース。
src/main/java/org/littlewings/jakarta/book/BookRepository.java
package org.littlewings.jakarta.book; import jakarta.data.repository.BasicRepository; import jakarta.data.repository.Repository; @Repository public interface BookRepository extends BasicRepository<Book, String> {}
JAX-RSリソースクラス。
src/main/java/org/littlewings/jakarta/book/BookResource.java
package org.littlewings.jakarta.book; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import jakarta.transaction.Transactional; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.GET; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; import java.util.List; @Path("/books") @ApplicationScoped @Transactional public class BookResource { @Inject private BookRepository bookRepository; @GET @Path("{isbn}") @Produces(MediaType.APPLICATION_JSON) public Book findByIsbn(@PathParam("isbn") String isbn) { return bookRepository.findById(isbn).orElse(null); } @GET @Produces(MediaType.APPLICATION_JSON) public List<Book> findAll() { return bookRepository.findAll().toList(); } @POST @Consumes(MediaType.APPLICATION_JSON) public void create(Book book) { bookRepository.save(book); } @POST @Path("fail") @Consumes(MediaType.APPLICATION_JSON) public void fail(Book book) { bookRepository.save(book); throw new RuntimeException("Oops!!"); } }
@Transactionalをつけていますが、ひとつロールバックさせるためのメソッドが混じっています。
JAX-RSの有効化。
src/main/java/org/littlewings/jakarta/RestApplication.java
package org.littlewings.jakarta; import jakarta.ws.rs.ApplicationPath; import jakarta.ws.rs.core.Application; @ApplicationPath("/") public class RestApplication extends Application {}
persistence.xml。
src/main/resources/META-INF/persistence.xml
<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="https://jakarta.ee/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_2.xsd" version="3.2"> <persistence-unit name="MyPersistenceUnit" transaction-type="JTA"> <jta-data-source>java:comp/env/datasources/MySqlDs</jta-data-source> <properties> <property name="hibernate.show_sql" value="true"/> <property name="hibernate.format_sql" value="true"/> <property name="jakarta.persistence.schema-generation.database.action" value="drop-and-create"/> </properties> </persistence-unit> </persistence>
いろいろ細工
ここまでに用意したコードをJava SE環境で動かすためにいろいろと細工していきます。
CDIを有効にするために、beans.xmlを空で作成します。
src/main/resources/META-INF/beans.xml
リポジトリーの実装クラスが必要とするEntityManagerFactoryをCDI管理Beanとして登録します。
src/main/java/org/littlewings/jakarta/environment/EntityManagerFactoryProducer.java
package org.littlewings.jakarta.environment; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.Dependent; import jakarta.enterprise.inject.Produces; import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.Persistence; @ApplicationScoped public class EntityManagerFactoryProducer { private EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("MyPersistenceUnit"); @Dependent @Produces public EntityManagerFactory createEntityManagerFactory() { return entityManagerFactory; } }
リポジトリーの実装クラスが期待するのは@PersistenceContextでのインジェクションなので、これをPortable Extensionsを
使って@Injectionでもできるようにします。
src/main/java/org/littlewings/jakarta/environment/PersistenceExtension.java
package org.littlewings.jakarta.environment; import jakarta.data.repository.Repository; import jakarta.enterprise.context.Dependent; import jakarta.enterprise.event.Observes; import jakarta.enterprise.inject.literal.InjectLiteral; import jakarta.enterprise.inject.spi.Extension; import jakarta.enterprise.inject.spi.ProcessAnnotatedType; import jakarta.enterprise.inject.spi.WithAnnotations; import jakarta.persistence.PersistenceUnit; import java.util.Arrays; public class PersistenceExtension implements Extension { public <T> void processRepositories(@Observes @WithAnnotations(Dependent.class) ProcessAnnotatedType<T> pat) { var annotatedType = pat.getAnnotatedType(); var clazz = annotatedType.getJavaClass(); if (Arrays.stream(clazz.getInterfaces()).anyMatch(c -> c.isAnnotationPresent(Repository.class))) { pat.configureAnnotatedType().fields().forEach(fieldConfigurator -> { if (fieldConfigurator.getAnnotated().isAnnotationPresent(PersistenceUnit.class)) { fieldConfigurator.add(InjectLiteral.INSTANCE); } }); } } }
src/main/resources/META-INF/services/jakarta.enterprise.inject.spi.Extension
org.littlewings.jakarta.environment.PersistenceExtension
このあたりは前のエントリーそのままですね。
Jakarta DataをJava SE環境で動かしてみる(Hibernate、Weld、Narayana、Agroal) - CLOVER🍀
データソースやJTAに関するリソースをJNDI登録します。
src/main/java/org/littlewings/jakarta/environment/ResourceInitializer.java
package org.littlewings.jakarta.environment; import com.arjuna.ats.arjuna.common.CoreEnvironmentBeanException; import com.arjuna.ats.arjuna.common.arjPropertyManager; import com.arjuna.ats.jta.TransactionManager; import com.arjuna.ats.jta.common.jtaPropertyManager; import com.arjuna.ats.jta.utils.JNDIManager; import io.agroal.api.AgroalDataSource; import io.agroal.api.configuration.supplier.AgroalConnectionFactoryConfigurationSupplier; import io.agroal.api.configuration.supplier.AgroalConnectionPoolConfigurationSupplier; import io.agroal.api.configuration.supplier.AgroalDataSourceConfigurationSupplier; import io.agroal.api.security.NamePrincipal; import io.agroal.api.security.SimplePassword; import io.agroal.narayana.NarayanaTransactionIntegration; import io.undertow.servlet.ServletExtension; import io.undertow.servlet.api.DeploymentInfo; import jakarta.servlet.ServletContext; import java.sql.SQLException; import javax.naming.InitialContext; import javax.naming.NamingException; public class ResourceInitializer implements ServletExtension { @Override public void handleDeployment(DeploymentInfo deploymentInfo, ServletContext servletContext) { installDataSource(); installJta(); } void installDataSource() { try { var initialContext = new InitialContext(); var agroalConnectionFactoryConfiguration = new AgroalConnectionFactoryConfigurationSupplier() .jdbcUrl( "jdbc:mysql://172.17.0.2:3306/practice?characterEncoding=utf-8&connectionCollation=utf8mb4_0900_bin") .credential(new NamePrincipal("kazuhira")) .credential(new SimplePassword("password")); var agroalConnectionPoolConfiguration = new AgroalConnectionPoolConfigurationSupplier() .connectionFactoryConfiguration(agroalConnectionFactoryConfiguration) .minSize(5) .maxSize(10) .transactionIntegration(new NarayanaTransactionIntegration( TransactionManager.transactionManager(), jtaPropertyManager.getJTAEnvironmentBean().getTransactionSynchronizationRegistry())); var agroalDataSourceConfiguration = new AgroalDataSourceConfigurationSupplier() .connectionPoolConfiguration(agroalConnectionPoolConfiguration); var agroalDataSource = AgroalDataSource.from(agroalDataSourceConfiguration); initialContext.bind("java:comp/env/datasources/MySqlDs", agroalDataSource); } catch (SQLException | NamingException e) { throw new RuntimeException(e); } } void installJta() { try { arjPropertyManager.getCoreEnvironmentBean().setNodeIdentifier("1"); jtaPropertyManager .getJTAEnvironmentBean() .setTransactionManagerJNDIContext("java:/jboss/TransactionManager"); jtaPropertyManager .getJTAEnvironmentBean() .setTransactionSynchronizationRegistryJNDIContext("java:/jboss/TransactionSynchronizationRegistry"); jtaPropertyManager.getJTAEnvironmentBean().setUserTransactionJNDIContext("java:comp/UserTransaction"); JNDIManager.bindJTAImplementation(); } catch (CoreEnvironmentBeanException | NamingException e) { throw new RuntimeException(e); } } }
登録先はSimple-JNDIです。
src/main/resources/jndi.properties
java.naming.factory.initial=org.osjava.sj.SimpleContextFactory org.osjava.sj.jndi.shared=true org.osjava.sj.jndi.ignoreClose=true
このあたりも前のエントリーからほとんど変わっていないのですが、どうやって初期化処理に組み込むかはちょっと困りました。
Jakarta DataをJava SE環境で動かしてみる(Hibernate、Weld、Narayana、Agroal) - CLOVER🍀
UndertowはDeploymentInfoを使わないとServletContainerInitializerやListenerを登録できないので、今回はUndertowの
ServletExtensionを使うことにしました。
Undertow / Undertow Servlet / Creating a Servlet Deployment
src/main/resources/META-INF/services/io.undertow.servlet.ServletExtension
org.littlewings.jakarta.environment.ResourceInitializer
NarayanaのObjectStoreの設定。完全にテスト用です。
src/main/resources/jbossts-properties.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"> <properties> <entry key="ObjectStoreEnvironmentBean.communicationStore.objectStoreType">com.arjuna.ats.internal.arjuna.objectstore.VolatileStore</entry> <entry key="ObjectStoreEnvironmentBean.objectStoreDir">${java.io.tmpdir}/ObjectStore</entry> </properties>
最後にmainクラスです。
src/main/java/org/littlewings/jakarta/App.java
package org.littlewings.jakarta; import jakarta.ws.rs.SeBootstrap; import java.util.concurrent.ExecutionException; import org.jboss.logging.Logger; public class App { static void main(String... args) throws ExecutionException, InterruptedException { var logger = Logger.getLogger(App.class); var configuration = SeBootstrap.Configuration.builder().host("0.0.0.0").port(8080).build(); var instance = SeBootstrap.start(new RestApplication(), configuration) .toCompletableFuture() .get(); logger.info("server startup."); System.console().readLine("> Enter stop."); instance.stop().toCompletableFuture().get(); } }
これで、先ほどのコードを動かせる準備ができました。
動かしてみる
では、動作確認してみましょう。
ビルドして実行。
$ mvn package $ java -jar target/app-0.0.1-SNAPSHOT.jar
ターミナルはEnterの入力待ちで停止します。
INFO: server startup. > Enter stop.
データを登録します。
$ curl localhost:8080/books --json '{"isbn": "978-4774183169", "title": "パーフェクト Java EE", "price": 3520}'
MySQLのログを確認すると、ちゃんとコミットしています。
2026-08-01T15:46:31.136781Z 11 Query SET autocommit=0 2026-08-01T15:46:31.137807Z 11 Query insert into book (isbn,price,title) values ('978-4774183169',3520,'パーフェクト Java EE') as tr on duplicate key update price = tr.price,title = tr.title 2026-08-01T15:46:31.138953Z 11 Query COMMIT 2026-08-01T15:46:31.156265Z 11 Query SET autocommit=1
データの確認。
$ curl -s localhost:8080/books/978-4774183169 | jq { "isbn": "978-4774183169", "price": 3520, "title": "パーフェクト Java EE" } $ curl -s localhost:8080/books | jq [ { "isbn": "978-4774183169", "price": 3520, "title": "パーフェクト Java EE" } ]
OKですね。
ロールバックするパターン。
$ curl localhost:8080/books/fail --json '{"isbn": "978-4297156589", "title": "作って学ぶ Spring Boot入門", "price": 2970}' Oops!!
MySQL側ではロールバックしていることが確認できます。
2026-08-01T15:48:26.289316Z 12 Query SET autocommit=0 2026-08-01T15:48:26.290223Z 12 Query insert into book (isbn,price,title) values ('978-4297156589',2970,'作って学ぶ Spring Boot入門') as tr on duplicate key update price = tr.price,title = tr.title 2026-08-01T15:48:26.291606Z 12 Query ROLLBACK 2026-08-01T15:48:26.303047Z 12 Query SET autocommit=1
アプリケーション側のスタックトレースも見てみます。
8月 02, 2026 12:48:26 午前 org.jboss.resteasy.core.providerfactory.DefaultExceptionMapper process ERROR: RESTEASY002375: Error processing request POST /books/fail - org.littlewings.jakarta.book.BookResource.fail java.lang.RuntimeException: Oops!! at org.littlewings.jakarta.book.BookResource.fail(BookResource.java:47) at org.littlewings.jakarta.book.BookResource$Proxy$_$$_WeldSubclass.fail$$super(Unknown Source) at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) at java.base/java.lang.reflect.Method.invoke(Method.java:565) at org.jboss.weld.interceptor.proxy.TerminalAroundInvokeInvocationContext.proceedInternal(TerminalAroundInvokeInvocationContext.java:55) at org.jboss.weld.interceptor.proxy.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:86) at com.arjuna.ats.jta.cdi.transactional.TransactionalInterceptorBase.invokeInOurTx(TransactionalInterceptorBase.java:181) at com.arjuna.ats.jta.cdi.transactional.TransactionalInterceptorBase.invokeInOurTx(TransactionalInterceptorBase.java:169) at com.arjuna.ats.jta.cdi.transactional.TransactionalInterceptorRequired.doIntercept(TransactionalInterceptorRequired.java:37) at com.arjuna.ats.jta.cdi.transactional.TransactionalInterceptorBase.intercept(TransactionalInterceptorBase.java:74) at com.arjuna.ats.jta.cdi.transactional.TransactionalInterceptorRequired.intercept(TransactionalInterceptorRequired.java:31) at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) at java.base/java.lang.reflect.Method.invoke(Method.java:565) at org.jboss.weld.interceptor.reader.SimpleInterceptorInvocation$SimpleMethodInvocation.invoke(SimpleInterceptorInvocation.java:75) at org.jboss.weld.interceptor.proxy.InterceptorMethodHandler.executeAroundInvoke(InterceptorMethodHandler.java:90) at org.jboss.weld.interceptor.proxy.InterceptorMethodHandler.executeInterception(InterceptorMethodHandler.java:73) at org.jboss.weld.interceptor.proxy.InterceptorMethodHandler.invoke(InterceptorMethodHandler.java:56) at org.jboss.weld.bean.proxy.CombinedInterceptorAndDecoratorStackMethodHandler.invoke(CombinedInterceptorAndDecoratorStackMethodHandler.java:80) at org.jboss.weld.bean.proxy.CombinedInterceptorAndDecoratorStackMethodHandler.invoke(CombinedInterceptorAndDecoratorStackMethodHandler.java:68) at org.littlewings.jakarta.book.BookResource$Proxy$_$$_WeldSubclass.fail(Unknown Source) at org.littlewings.jakarta.book.BookResource$Proxy$_$$_WeldClientProxy.fail(Unknown Source) at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) at java.base/java.lang.reflect.Method.invoke(Method.java:565) at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:154) at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:118) at org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:560) at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:452) at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$0(ResourceMethodInvoker.java:413) at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:333) at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:415) at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:378) at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:356) at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:70) at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:434) at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$0(SynchronousDispatcher.java:245) at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:159) at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:333) at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:162) at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:234) at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:222) at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:55) at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:51) at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:614) at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74) at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62) at io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68) at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36) at io.undertow.servlet.handlers.RedirectDirHandler.handleRequest(RedirectDirHandler.java:68) at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:117) at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57) at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46) at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64) at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60) at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:75) at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43) at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) at io.undertow.servlet.handlers.SendErrorPageHandler.handleRequest(SendErrorPageHandler.java:52) at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:278) at io.undertow.servlet.handlers.ServletInitialHandler$1.call(ServletInitialHandler.java:131) at io.undertow.servlet.handlers.ServletInitialHandler$1.call(ServletInitialHandler.java:128) at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48) at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43) at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:258) at io.undertow.servlet.handlers.ServletInitialHandler.lambda$new$1(ServletInitialHandler.java:100) at io.undertow.server.Connectors.executeRootHandler(Connectors.java:418) at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:938) at org.jboss.threads.ContextHandler$1.runWith(ContextHandler.java:18) at org.jboss.threads.EnhancedQueueExecutor$Task.doRunWith(EnhancedQueueExecutor.java:2691) at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2670) at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1615) at org.xnio.XnioWorker$WorkerThreadFactory$1$1.run(XnioWorker.java:1282) at java.base/java.lang.Thread.run(Thread.java:1474)
ちゃんとNarayanaのインターセプターが入っていることが確認できます。
at org.jboss.weld.interceptor.proxy.TerminalAroundInvokeInvocationContext.proceedInternal(TerminalAroundInvokeInvocationContext.java:55) at org.jboss.weld.interceptor.proxy.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:86) at com.arjuna.ats.jta.cdi.transactional.TransactionalInterceptorBase.invokeInOurTx(TransactionalInterceptorBase.java:181) at com.arjuna.ats.jta.cdi.transactional.TransactionalInterceptorBase.invokeInOurTx(TransactionalInterceptorBase.java:169) at com.arjuna.ats.jta.cdi.transactional.TransactionalInterceptorRequired.doIntercept(TransactionalInterceptorRequired.java:37) at com.arjuna.ats.jta.cdi.transactional.TransactionalInterceptorBase.intercept(TransactionalInterceptorBase.java:74) at com.arjuna.ats.jta.cdi.transactional.TransactionalInterceptorRequired.intercept(TransactionalInterceptorRequired.java:31)
データも増えていませんね。
$ curl -s localhost:8080/books | jq [ { "isbn": "978-4774183169", "price": 3520, "title": "パーフェクト Java EE" } ]
OKです。
おわりに
SeBootstrapで起動したRESTEasy上で、Jakarta Data(Hibernate)とJTA(Narayana CDI)を動かしてみました。
前のエントリーでJava SE環境でJakarta Dataを動かせていたので、あまり苦労はしないだろうと思っていたのですが、
ほぼ予想どおりでした。
唯一予定外だったのは、JNDIリソースの登録をServletContainerInitializerでやろうと思っていたのに、Undertowでは
動かせなかったことですね。
CDIのPortable Extensionを使うという案もあったのですが、ちょっと違うかなと…。
ひとまず、やりたいことはできたので良しとしましょう。ふつうは使うことはないと思いますけど(笑)。