JavalinでWebアプリケーションを構築する

[Javalin](https://javalin.io/)というフレームワークを試してみたので投稿します。

このフレームワークは、参考書や日本語のサイトなどの情報が少ないですが、DocsとTutorialがとてもわかりやすく、特にTutorialには、それぞれサンプルプログラムがGitHUBに投稿されているので、Intellijにそのままインポートしてカスタマイズできたりしますので、WEBアプリを作成するのにあまり困らないでしょう。

# 環境

| カテゴリ       |       値 |
|:-----------------|------------------:|
| os               |windows 10 home 64bit | 
| Java|            1.8 |
| framework|                Javalin 3.6| 
| 開発環境|                IntelliJ IDEA 2019.2| 

#1. Hello World

Gradleプロジェクトを新規作成して、以下を実装するだけです。JavaがインストールされていればWeb Serverも立ち上がります。Tomcatも必要ありません。

```Java:HelloWorld.java
import io.javalin.Javalin;

public class HelloWorld {
    public static void main(String[] args) {
        Javalin app = Javalin.create().start(7000);
        app.get("/", ctx -> ctx.result("Hello World"));
    }
}
``` 

#2. Tutorial(Basic website structure)

実践では、Tutorialのサンプルプログラムをダウンロードし、カスタマイズするようなアプローチがいいでしょう。
「Basic website structure」[tipsy/javalin-website-example](https://github.com/tipsy/javalin-website-example)をダウンロードして、Intellijで開いてみます。

```text:project.tree
└─src
    └─main
        ├─java
        │  └─app
        │      │  Main.java
        │      ├─book
        │      │      Book.java
        │      │      BookController.java
        │      │      BookDao.java
        │      ├─index
        │      │      IndexController.java
        │      ├─login
        │      │      LoginController.java
        │      ├─user
        │      │      User.java
        │      │      UserController.java
        │      │      UserDao.java
        │      └─util
        │              Filters.java
        │              HerokuUtil.java
        │              MessageBundle.java
        │              Path.java
        │              RequestUtil.java
        │              ViewUtil.java
        └─resources
            ├─localization
            │      messages_de.properties
            │      messages_en.properties
            ├─public
            │  │  main.css
            │  └─img
            │          english.png
            │          favicon.png
            │          german.png
            │          logo.png
            ├─velocity
            │  │  layout.vm
            │  │  notFound.vm
            │  ├─book
            │  │      all.vm
            │  │      one.vm
            │  ├─index
            │  │      index.vm
            │  └─login
            │          login.vm
            └─velocityconfig
                    velocity_implicit.vm
```

Mainを実行するとそのまま動きます。あとはDAOなどのクラスをデータベースに変換するだけで、プロジェクトテンプレートを作成することができます。

#3. Thymeleaf

さきほどインポートしたプロジェクトのテンプレートエンジンは「velocity」になっています。Thymeleafを使いたい場合は変更が必要です。「javalin」は、thymeleafもデフォルトでサポートしているようですが、dialectに対応していないようなので、カスタマイズしましょう。

htmlのルートは、「/public/templates」とします。以下の構成でファイルを作成していきます。
layout.htmlで、layout-dialectを使ってhtmlのレイアウトを共通化します。

```
└─src
    ├─main
    │  ├─java
    │  │  └─app
    │  │          AppThymeleafRenderer.java
    │  │          Main.java
    │  └─resources
    │      ├─public
    │      │  └─css
    │      │         main.css
    │      └─templates
    │          │  layout.html
    │          └─example
    │                  index.html
```


(1) gradle.gradleに以下を追加します。
※Projectは、gradleで焼き直ししています

```gradle
    compile group: 'org.thymeleaf', name: 'thymeleaf', version: '3.0.11.RELEASE'
    compile group: 'nz.net.ultraq.thymeleaf', name: 'thymeleaf-layout-dialect', version: '2.4.1'
    compile "org.webjars:jquery:3.4.1"
``` 

(2) ThymeleafRenderer

Thymeleafは、「layout-dialect」を使う場合などカスタマイズする場合独自のrendererを作成する必要があります。

```java:AppThymeleafRenderer.java

import io.javalin.http.Context;
import io.javalin.plugin.rendering.FileRenderer;
import nz.net.ultraq.thymeleaf.LayoutDialect;
import org.jetbrains.annotations.NotNull;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.WebContext;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
import org.thymeleaf.templateresolver.ITemplateResolver;

import java.util.Map;

public class AppThymeleafRenderer implements FileRenderer {

    private final TemplateEngine templateEngine;

    public AppThymeleafRenderer() {
        templateEngine = templateEngine();
    }

    @Override
    public String render(@NotNull String filePath, @NotNull Map<String, Object> model, @NotNull Context ctx) {
        WebContext context =
                new WebContext(ctx.req, ctx.res, ctx.req.getServletContext(), ctx.req.getLocale());
        context.setVariables(model);
        return templateEngine.process(filePath, context);
    }

    private TemplateEngine templateEngine() {
        TemplateEngine templateEngine = new TemplateEngine();
        templateEngine.setTemplateResolver(templateResolver());
        // LayoutDialectを追加する。
        templateEngine.addDialect(new LayoutDialect());
        return templateEngine;
    }

    private ITemplateResolver templateResolver() {
        ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
        // /templatesフォルダをルートにする。
        templateResolver.setPrefix("/templates/");
        return templateResolver;
    }

}
```

(3) Main.javaに追加

Mainメソッドで、「JavalinRenderer」に、先に作成した「AppThymeleafRenderer」クラスを追加します。
また、Routerでhtmlを返す場合は、「ctx.render」とします。

```Java:Main.java

public class Main {
    public static void main(String[] args) {

//add
        JavalinRenderer.register(new AppThymeleafRenderer(), ".html");
//add end
        Javalin app = Javalin.create(config->{
            config.enableWebjars();
            config.addStaticFiles("/public");
        }).start(7000);
//add
        app.get("/template", ctx -> {
            Map<String, Object> model = new HashMap<>();
            model.put("hello", "hello world");
            ctx.render("/example/index.html", model);
        });
//add end
```

(4) layout.html

```html:layout.html
<!DOCTYPE html>
<html lang="ja" xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
    <meta charset="UTF-8">
    <title>Spring Boot Sample Site</title>
    <meta name="description" content="common-meta">
    <script th:src="@{/webjars/jquery/3.4.1/jquery.min.js}"></script>
    <link rel="stylesheet" href="css/main.css">
</head>
<body>
<div class="container">
    <div class="inner">
        <div class="body header">
            <div class="apptitle">Example</div>
        </div>
        <div class="body main">
            <div layout:fragment="contents"></div>
        </div>
        <div class="body footer">
            <footer style="text-align:center;">共通フッタ</footer>
        </div>
    </div>
</div>
</body>
</html>
```

```html:index.html
<!DOCTYPE html>
<html
        xmlns:th="http://www.thymeleaf.org"
        xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
        layout:decorate="~{/layout.html}">
<head></head>
<body>
<div layout:fragment="contents">
    <div th:text="${hello}">hello</div>
</div>
</body>
</html>
```

[http://localhost:7000/template](http://localhost:7000/template)にアクセスすると、layoutも正しく出力されてます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246741/6d4aeb1b-ce51-e347-1e78-e663db0a65fa.png)


#4. Database

いろいろ悩んだ末、「Apache commons-dbutils」を使うことにしました。理由は単純にわかりやすいので。Connection Poolは、「HikariCP」、Databaseには「postgresql」を使用します。

```build.gradle
    compile "com.zaxxer:HikariCP:2.7.3"
    compile "org.postgresql:postgresql:42.2.8"
    compile "commons-dbutils:commons-dbutils:1.7"
```

```java:PGDataSource.java

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import javax.sql.DataSource;

public class PGDataSource {
    private static HikariDataSource ds = null;
    public PGDataSource(){
        HikariConfig hikariconfig = new HikariConfig();
        hikariconfig.setUsername("testuser");
        hikariconfig.setPassword("************************");
        hikariconfig.setJdbcUrl("jdbc:postgresql://localhost:5432/javalindb");
        hikariconfig.setMaximumPoolSize(2);
        ds=new HikariDataSource(hikariconfig);
    }
    public DataSource getDataSource(){
        return ds;
    }
    public void close(){
        if (!ds.isClosed()) ds.close();
    }

}
```

```java:User.java
import lombok.Data;

@Data
public class User {
    private String username;
    private String password;
    public User(){};
    public User(String username, String password){
        this.username=username;
        this.password=password;
    };
}
```

```Java:Main.java
public class Main {

//add
    private static PGDataSource ds;
    public static Connection getConnection() throws SQLException {
        Connection connection = ds.getDataSource().getConnection();
        return connection;
    }
//add end
    public static void main(String[] args) {
        JavalinRenderer.register(new AppThymeleafRenderer(), ".html");
//add
        ds = new PGDataSource();
//add end
//・・・・・・・
//add
        app.get("/user", ctx -> {
            QueryRunner runner = new QueryRunner();
            ResultSetHandler rsh = new BeanListHandler(User.class);
            List<User> users = (List<User>) runner.query(getConnection(),
                    "select username from public.users",rsh);
            ctx.json(users);
        });
//add end
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246741/63bc14b1-0489-441c-b7a5-434c406284b3.png)

これだけわかれば、あとはDocsとTutorialを見ていけばWEBアプリを作成できます。

#5. 参考サイト

[Javalinを調べたときのメモ](https://qiita.com/rubytomato@github/items/80ab1d685641449009c1)
[nodchipの日記-Apache Commons DbUtils](https://nodchip.hatenadiary.org/entry/20120211/1328952012)