メンチカツには醤油でしょ!!

AWS/Java/Node.js/Spreadsheets/Docker/Jenkins/コミュニティ・勉強会レポを主とした技術系ブログ

Struts2をTomcat⇒Wildfly10に移行してEE環境(CDI+EJB+JPA)にしたら動かなくなった

上手に書けたら…と言いつつ上手に書けてはいないのですが、将来追記するかも。
放置な理由はテザリング回線が帯域制限で6月後半はほとんど書けなかったわけです。
46時間近くも動画流しっぱなしにしてたらそりゃ帯域制限かかりますわな。

言い訳多いですが本題へ。

before

Tomcat8 + Struts2 + JPA 2.0 (EclipseLink 入れた)

after

Wildfly 10 + Struts2CDI 1.2 (Weld 2.2) + EJB 3.2 + JPA 2.1 (標準Hibernate)
と、Struts2以外はWildfly10標準そのままなのです。

マイグレ理由

・社内展開が見込まれたのでEE環境へ移行してCDI,EJBの恩恵を受けまくりたい

ちなみにJSFにしなかった理由は
・展開時のスタディコストの軽減 (ゼロにはならないけど)
JSFは結局Prime Facesがデファクトで必須になる感あったり
って感じの理由で戦略的に選択しなかったつもり。MVC 1.0カモン!

悩んだこと

よくわからない(って言っちゃいけない)けど2,3日ぐらい消費してしまった。
@EJBや@InjectできずNullPointerException
@PersistenceUnitも注入されない…
相談できる相手も居なかったのでここだけの話 地獄でした。

WELDのエラーとStackOverflowとにらめっこ。
ひととおりの答えでは全然解決しない。
でも全然Injectできない…

そもそもServletCDIはビジネス層では関係ないでしょ…JSF必須なの?とか思ったり、でもWildfly起動時のログではちゃんとCDIに登録されている模様だし。

Injectできない…ツラい!悪循環。

やったこと

まずはTomcatで動いていたソースをそのまま移行。

Maven pom.xmlの整理。
ここでTomcat時代にはお手製で組み込んでいたJTA/JPAをprovidedにしたり
JPAをEclipseLink⇒Hibernate(Wildfly標準)に変更したり
EEのdependencyを組み込んで
<scope>provided</scope>を入れたりします。

そしてソースコード
・対象クラスに@Statelessを付与、EJB内ではEntityManagerを@Inject
・ActionクラスからはEJBクラスのnewを@Inejctに書き換え
だが動かない。
ビジネスロジックInjectできない。。。

Webとビジネスロジックは別のMavenサブモジュールに配置していたので
試しに同じサブモジュールに配置してもこれまたダメ…

CDI 1.1以降本当は必要ないbeans.xml
ParentMavenProject/WebProject/src/main/webapp/WEB-INF/beans.xml
に配置してみたりしてもダメ。
(ちなみにsrc/main/resources/META-INF/beans.xmlでも良かったがjboss-deployment-structure.xmlやweb.xmlがあるのでwebapp/WEB-INF配下にした。)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
       bean-discovery-mode="annotated">
</beans>
<!-- http://n-agetsuma.hatenablog.com/entry/2013/08/18/130358 より引用 -->

でも

YYYY-MM-DD HH:MM:SS,SSS INFO [org.jboss.as.ejb3.deployment] (MSC service thread 1-3) WFLYEJB0473: JNDI bindings for session bean named '【EJBサービス名】' in deployment unit 'deployment "【アプリ名】.war"' are as follows:
java:global/【アプリ名】/【EJBサービス名】!【パッケージ名】.【EJBサービス名】
java:app/【アプリ名】/【EJBサービス名】!【パッケージ名】.【EJBサービス名】
java:module/【EJBサービス名】!【パッケージ名】.【EJBサービス名】
java:global/【アプリ名】/【EJBサービス名】
java:app/【アプリ名】/【EJBサービス名】
java:module/【EJBサービス名】

な感じなのでWFLYEJB0473でCDIには登録されている模様。

試しにWildfly 8.2.1.Final も入れて動かしてみるも同じ。。

でも新規でmaven-archetypeよりWildfly-CDI-JPA入りのものを落としてくるとそれは動く。。
もぅ…
ちなみに使ったのは
br.com.address.archetypes:struts2-archetype:1.5
Descriptionが
an archetype web 3.0 + struts2 (bootstrap + jquery) + JPA 2.1 with struts2 login system
といった所、かなり安心感ありますw

Struts2 CDI Pluginなるものを発見

既にEE環境なのになぜpluginでCDIを入れなければいけないのかという葛藤は置いてとけないけどぐっとこらえて‥

[ 2779 ] [Struts2]Struts2+Tomcat7でstruts2-cdiプラグインを使う ::: Serendipity 2 future lies'n sundome. (´・ω・)

でも動かず...

そしてまだまだ色々やってるうちに動くように

ごめんなさいごめんなさい。

よくよく振り返ると

n-agetsuma.hatenablog.com

に記載があるように

スコープとしては暗黙的に@Dependentとみなされますが、明示的にスコープアノテーションが付与されていないため、インジェクション対象とならない為です。

bean-discovery-mode="annotated"の場合@Dependentだけだとダメよと。

今回は

public class Action {
    @EJB
    EJBService service;
}

@Stateless
public class EJBService {
    @Inject
    private EntityManager em;
}

@Named
@Dependent
public class EntityManagerProducer {
    @PersistenceContext(unitName="hogehoge")
    @Produces
    private EntityManager em;
}

だったのですがここらへんが一番アヤシイ感じではあります。
反省点としてはどこだったんだろう…と。。

同じ悩みの方は、多分もうドハマリ中だと思うので
新規でMaven archetype (br.com.address.archetypes:struts2-archetype:1.5)から
現在の実装に寄せていくと動くようになると思います。(勝手…)