kagamihogeの日記

kagamihogeの日記です。

WildFly 11 + Spring Boot

直近でWildFly 11でSpring Boot動かすのに調べたことのメモ。といっても、WildFlyプラスそのプロジェクト固有事情による設定とかの内容も混じっている。ただまぁ、自分用の作業ログなので余りそのへんは気にしない。

ソースコード

https://github.com/kagamihoge/wildflyspringboot

環境

  • jdk1.8.0_152
  • Eclipse 4.7.1
  • WildFly 11.0.0.Final
  • spring-boot 1.5.9.RELEASE

とりあえず動かす

とりあえず https://projects.spring.io/spring-boot/#quick-start コピペして動かす。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.kagamihoge</groupId>
    <artifactId>wildflyspringboot</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.9.RELEASE</version>
    </parent>

    <name>wildflyspringboot</name>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>


    <properties>
        <java.version>1.8</java.version>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
package com.kagamihoge.wildflyspringboot.controller;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@EnableAutoConfiguration
public class SampleController {

    @RequestMapping("/")
    @ResponseBody
    String home() {
        return "Hello World!";
    }

    public static void main(String[] args) throws Exception {
        SpringApplication.run(SampleController.class, args);
    }
}

起動して http://localhost:8080/ でアクセスできるのを確認する。

Automatic restart

ソースコード編集すると自動反映されるヤツをいれる。

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>

組み込みサーバーをUndertowにする

73.13 Use Undertow instead of Tomcat

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
            <exclusion>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-tomcat</artifactId>
            </exclusion>
        </exclusions>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-undertow</artifactId>
        </dependency>

Thymeleaf

Spring Boot で Thymeleaf 使い方メモを参考に設定。

warの生成とdynamic web projectとしても実行できるようにする

Spring Bootをeclipseのdynamic web projectとして実行する - kagamihogeの日記

最終的な成果物はwarになるので変更する。また、プロジェクト固有の事情で、Eclipseのserverビューから起動するヤツも併用可能にした。

finalName

このままmvn packageとかするとwildflyspringboot-0.0.1-SNAPSHOT.warみたいな名前のwarになる。なのでpom.xmlにfinalNameを指定する。

    <build>
        <finalName>wildflyspringboot</finalName>
        ...
    </build>

コンテキストパス

warの名前と合わせるために、組み込みコンテナで起動したときにもhttp://localhost:8080/wildflyspringboot/みたいなコンテキストパスでアクセスさせる。

src/main/resources/application.yml を作成して以下を追加する。

server:
  contextPath: /wildflyspringboot

logging Per-deployment Loggingでlog4j

WildFlyPer-deployment Logginglog4jを使うのでsrc/main/resources/log4j.xmlを作成。今時log4jなのはプロジェクト固有事情。

組み込みコンテナでlog4が使えなかった

組み込みコンテナだとlog4jが動かなかった。あまり突っ込んで調査しなかったが、logbackは動くのでまぁいっかと妥協した。

プロパティファイル

spring-bootのプロファイルを使用してプロファイルを切り替え。とりあえず、ここでは以下の二種類のファイルを作ったとして話を進める。

/src/main/resources/
    application-dev.yml
    application-procuction.yml

mavenのプロファイルでspring-bootのプロファイルを切り替える

プロジェクト固有事情でmavenのビルド時にプロパティファイルを切り替える必要があった。つまりmvn package -P productionとかやる。なのでmavenのプロファイルをspring-bootのプロファイルに連動させる必要があった。

更なる縛りプレイとして、政治的事情で、WildFlyをいじれない。単一war内で完結する必要がある。つまり、コマンドライン引数・JNDI・システムプロパティ・OS環境変数、でspring-bootのプロファイル指定が出来ない。ぶっちゃけかなり困った。

で、spring-bootのリファレンスExternalized Configurationを見て残ったのがServletContextの初期化パラメータでプロファイルを指定する方法。詳細は Spring Bootでweb.xmlのcontext-param経由でspring.profiles.activeを指定 を参照。

まず、web.xmlのcontext-paramにspring-bootのプロファイルを指定する、mavenプレースホルダを記述する。mavenビルド時にこのプレースホルダが、ビルド時に指定されたmavenのプロファイルで書き換えられる。これにより、mavenのプロファイルとspring-bootのプロファイルを連動した。

ぶっちゃけかなり面倒。いま思うとsprin-bootのプロファイルにこだわなくても良かったかなぁ……とも思う。

プロファイルの切り替え(動的webプロジェクト)

eclipseの動的webプロジェクトとして動かす場合、Servers -> WildFly -> Open launch configuration -> VM argumentsに-Dspring.profiles.active=devとか追加する。

f:id:kagamihoge:20171230141014j:plain

プロファイルの切り替えのやり方は色々あるんで、とりあえず一例として。

データソース

まず依存性を追加する。

     <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

WildFlyのJNDI経由の場合

Connection to a JNDI DataSource の通り、application-production.ymlに以下のような決め打ちのプロパティ名で設定を追加すると勝手にDataSourceを設定してくれる。

spring:
  datasource:
    jndi-name: java:/PostgresDS

組み込みコンテナ(Undertow)の場合

Tomcatとは異なりUndertowにはJNDIは無い。ので、DataSourceを自前で設定する。これも以下のような決め打ちのプロパティ名があるのでそれをapplication-dev.ymlに追加する。参照:29.1.2 Connection to a production database

spring:
  datasource:
    url: jdbc:postgresql://192.168.10.23:5432/testdb
    username: postgres
    password: a

PostgreSQLjdbcドライバが無いって怒られるので、適当なバージョンのものをpom.xmlに追加する。

     <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>42.1.4</version>
        </dependency>

動作確認するにはDataSourceをDIしたり、JdbcTemplateをDIして適当なクエリを発行してみる。

   @Autowired
    JdbcTemplate jdbcTemplate;

        int result1 =jdbcTemplate.queryForObject("select 1", Integer.class);
        logger.info("select 1="+result1);

validation

Spring Getting Started - Validating Form Inputあたりを参考に設定する。

     <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
        </dependency>
   @GetMapping("hello2")
    public String hello2(@RequestParam(name="param") @NotEmpty @NotNull String param) {
        logger.info("param=" + param);
        return "hello";
    }

ユニットテスト

spring-framework上でのユニットテストはこれまで通り。spring-boot-starter-testがspring-bootのテスト用ライブラリに加えてMockitoやらHamcrestやらメジャーどころも入れてくれるので追加する。

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

spring-bootアプリケーションそのもののユニットテスト

package com.kagamihoge.wildflyspringboot;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringRunner;

import com.kagamihoge.wildflyspringboot.ApplicationTest.MyTestsConfiguration;

@RunWith(SpringRunner.class)
@SpringBootTest(properties = {"spring.profiles.active=test"})
@Import(MyTestsConfiguration.class)
public class ApplicationTest {

    @Test
    public void test() {
        
    }
    
    @TestConfiguration
    static class MyTestsConfiguration {
        
    }

}

@TestConfigurationを付与する内部クラスで、ユニットテスト用のconfigクラスを作成。

@Import(MyTestsConfiguration.class)でそのconfigクラスをインポート。

@SpringBootTest(properties = {"spring.profiles.active=test"})で、spring-bootアプリケーションとしてのテストを指定し、spring-bootのプロファイルにtestを指定。src/test/java/application-test.ymlを作っておけばそれが使われる。