読者です 読者をやめる 読者になる 読者になる

かんがるーさんの日記

最近自分が興味をもったものを調べた時の手順等を書いています。今は Spring Boot をいじっています。

Spring Boot でメール送信する Web アプリケーションを作る ( その10 )( DataSource Bean の独自定義は不要?他 )

概要

Spring Boot でメール送信する Web アプリケーションを作る ( その9 )( メール送信画面の作成4 ) の続きです。

  • いろいろ修正したい部分があったので、メール送信画面の作成を一時中断して修正作業を行います。

  • 今回の手順で確認できるのは以下の内容です。

    • 以下の点を調査・修正します。
      • Spring Boot でメール送信する Web アプリケーションを作る ( 番外編 )( DataSource Bean に application.properties の設定が反映される仕組みとは? )で DataSourceAutoConfiguration.java で DataSource Bean が自動生成されることに気づいたので、ApplicationConfig クラスで定義しなくてもよいか確認します。
      • DataSourceAutoConfiguration.java を見て、インナークラスに @Configuration アノテーションを付加し、その中で Bean を生成出来ることに気づいたので、今の DomaBeanConfig クラスを DomaConfig クラスのインナークラスに出来ないか確認します。DomaBeanConfig クラスと DomaConfig クラスは出来れば1つにしたいと考えていました。
      • MAIL001MailBuilder クラスを以下のように作り直します。
        • package を ksbysample.webapp.email.web.mailsend → ksbysample.webapp.email.helper.mail へ移動します。
        • クラス名を MAIL001MailBuilder → MAIL001MailHelper へ変更します。
        • MAIL001MailHelper クラスに @Component アノテーションを付加します。
        • Velocity のテンプレートファイルのパスをセットするフィールド templateLocation は MAIL001MailHelper クラス内で private final String で定数として定義します。
        • VelocityUtils クラスをメソッドでセットするのではなく @Autowired で DI させます。
        • build メソッドを削除し、create メソッドメソッド名を createMessage へ変更します。
        • 変数 mailsendForm は createMessage メソッドの引数で受け取ります。
      • Doma-Gen で自動生成した Dao クラス、Entity クラスのファイルを Gradle の gen タスク内で git add させる方法があるのか調査します。

ソフトウェア一覧

参考にしたサイト

  1. Gradle Goodness: Use Git Commit Id in Build Script
    http://mrhaki.blogspot.jp/2015/04/gradle-goodness-use-git-commit-id-in.html

    • build.gradle 内で git レポジトリにアクセスする方法 ( Grgit を使用する ) を参考にしました。
  2. GitHub - grgit
    https://github.com/ajoberstar/grgit

  3. grgit 1.1.0 API
    http://ajoberstar.org/grgit/docs/groovydoc/index.html

  4. domaframework/master/build.gradle
    https://github.com/domaframework/master/blob/master/build.gradle

    • grgit の使用例を探していたら DomaGitHub の build.gradle がヒットしました。
    • 使用されているのが grgit 0.8.0 のため grgit 1.1.0 だとエラーが出てそのままでは使えませんが、使い方の参考にさせていただきました。

手順

DataSource Bean を定義する必要はないのではないか?

  1. IntelliJ IDEA で 1.0.x-delete-datasource-bean ブランチを作成します。

  2. src/main/java/ksbysample/webapp/email/config の下の ApplicationConfig.javaリンク先の内容 に変更します。

  3. 動作確認します。Gradle projects View から bootRun タスクを実行して Tomcat を起動します。正常に起動して "Started Application in ..." のログが出力されることが確認できます。

  4. ブラウザを起動し http://localhost:8080/mailsend へアクセスします。以下の画像の値を入力後、「送信」ボタンをクリックします。

    f:id:ksby:20150505091551p:plain

  5. エラーは発生せず、メールも送信されました。email, email_item どちらのテーブルにもデータが保存されていました。DataSource Bean の定義を削除しても問題ないようです。

  6. Run View で Ctrl+F2 を押して Tomcat を停止します。

  7. commit、GitHub へ Push、1.0.x-delete-datasource-bean -> 1.0.x へ Pull Request、1.0.x でマージ、1.0.x-delete-datasource-bean ブランチを削除、をします。

DomaBeanConfig クラスを DomaConfig クラスのインナークラスとして定義できるか?

  1. IntelliJ IDEA で 1.0.x-change-innerclass-domabeanconfig ブランチを作成します。

  2. src/main/java/ksbysample/webapp/email/config の下の DomaConfig.javaリンク先の内容 に変更します。

  3. src/main/java/ksbysample/webapp/email/config の下の DomaBeanConfig.java を削除します。

  4. 動作確認します。Gradle projects View から bootRun タスクを実行して Tomcat を起動します。正常に起動して "Started Application in ..." のログが出力されることが確認できます。

  5. ブラウザを起動し http://localhost:8080/mailsend へアクセスします。「DataSource Bean を定義する必要はないのではないか?」の画像のテストデータの値を入力後、「送信」ボタンをクリックします。

  6. エラーは発生せず、メールも送信されました。email, email_item どちらのテーブルにもデータが保存されていました。DomaBeanConfig クラスを DomaConfig クラスのインナークラスで定義しても問題ないようです。

  7. Run View で Ctrl+F2 を押して Tomcat を停止します。

  8. commit、GitHub へ Push、1.0.x-change-innerclass-domabeanconfig -> 1.0.x へ Pull Request、1.0.x でマージ、1.0.x-change-innerclass-domabeanconfig ブランチを削除、をします。

MAIL001MailBuilder クラスを作り直す

  1. IntelliJ IDEA で 1.0.x-remake-mail001mailbuilder ブランチを作成します。

  2. src/main/java/ksbysample/webapp/email/web/mailsend の下の MAIL001MailBuilder を選択後、Shift+F6 を押して「Rename」ダイアログを表示した後、MAIL001MailHelper へリネームします。

    f:id:ksby:20150513015718p:plain

  3. src/main/java/ksbysample/webapp/email/web/mailsend の下の MAIL001MailHelper を選択後、F6 を押して「Move」ダイアログを表示した後、ksbysample.webapp.email.helper.mail へ移動します。

    f:id:ksby:20150513001411p:plain

    上のダイアログで「Refactor」ボタンを押すと確認ダイアログが表示されて「Yes」ボタンを押すと存在しない package を作成してくれます。

    f:id:ksby:20150513001506p:plain

  4. src/main/java/ksbysample/webapp/email/helper/mail の下の MAIL001MailHelper.javaリンク先の内容 に変更します。

  5. src/main/java/ksbysample/webapp/email/web/mailsend の下の MailsendService.javaリンク先の内容 に変更します。

  6. 動作確認します。Gradle projects View から bootRun タスクを実行して Tomcat を起動します。

  7. ブラウザを起動し http://localhost:8080/mailsend へアクセスします。「DataSource Bean を定義する必要はないのではないか?」の画像のテストデータの値を入力後、「送信」ボタンをクリックします。

  8. エラーは発生せず、メールも送信されました。email, email_item どちらのテーブルにもデータが保存されていました。

  9. Run View で Ctrl+F2 を押して Tomcat を停止します。

  10. commit、GitHub へ Push、1.0.x-remake-mail001mailbuilder -> 1.0.x へ Pull Request、1.0.x でマージ、1.0.x-remake-mail001mailbuilder ブランチを削除、をします。

Doma-Gen で自動生成したファイルを自動で git add する

  1. IntelliJ IDEA で 1.0.x-gitadd-domagenfile ブランチを作成します。

  2. build.gradle を リンク先の内容 に変更します。

  3. Gradle projects View の左上にある「Refresh all Gradle projects」アイコンをクリックして、変更した build.gradle の内容を反映します。

  4. 動作確認します。最初に src/main/java/ksbysample/webapp/email の下の dao の下に TestDao インターフェースを、entity の下に TestEntity クラスを手動で作成します。この時「Add File to Git」ダイアログが出たら「No」ボタンをクリックして追加しないようにします。

    f:id:ksby:20150513065315p:plain

    f:id:ksby:20150513065402p:plain

    f:id:ksby:20150513065639p:plain

  5. この時点でメインメニューから「VCS」-「Commit Changes...」を選択して「Commit Changes」ダイアログを表示しても TestDao, TestEntity は表示されません。「Cancel」ボタンをクリックしてダイアログを閉じます。

    f:id:ksby:20150513070033p:plain

  6. 次に gen タスクを実行します。

    f:id:ksby:20150513070152p:plain

  7. 再度メインメニューから「VCS」-「Commit Changes...」を選択して「Commit Changes」ダイアログを表示すると、今度は git add されているので表示されます。「Cancel」ボタンをクリックしてダイアログを閉じます。

    f:id:ksby:20150513070332p:plain

  8. 作成した TestDao インターフェース、TestEntity クラスを削除します。

  9. commit、GitHub へ Push、1.0.x-gitadd-domagenfile -> 1.0.x へ Pull Request、1.0.x でマージ、1.0.x-gitadd-domagenfile ブランチを削除、をします。

    • commit 時に Code Analysis ダイアログが表示されますが、表示される Warning 「Unnecessary qualified reference」と「'open' in 'org.ajoberstar.grgit.Grgit' cannot be applied to (['dir':java.io.File])」の対処方法が分からなかったため、「Commit」ボタンをクリックして先に進めます。

次回は。。。

  • メール送信画面で作成したモジュールのテストを書きます。
  • メールの unit test 用のライブラリもあるようなので、使用してみる予定です。

ソースコード

ApplicationConfig.java

package ksbysample.webapp.email.config;

import org.springframework.context.annotation.Configuration;

@Configuration
public class ApplicationConfig {
}
  • DataSource Bean の定義を削除します。
  • ApplicationConfig.java は Bean 定義用としてそのまま残しておきます。

DomaConfig.java

package ksbysample.webapp.email.config;

import org.seasar.doma.jdbc.Config;
import org.seasar.doma.jdbc.dialect.Dialect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;

@Component
public class DomaConfig implements Config {

    private DataSource dataSource;

    @Autowired
    private Dialect dialect;

    @Autowired
    public void setDataSource(DataSource dataSource) {
        this.dataSource = new TransactionAwareDataSourceProxy(dataSource);
    }

    @Override
    public DataSource getDataSource() {
        return this.dataSource;
    }

    @Override
    public Dialect getDialect() {
        return this.dialect;
    }

    @Configuration
    protected static class DomaBeanConfig {

        @Value("${doma.dialect}")
        private String domaDialect;

        @Bean
        public Dialect dialect()
                throws ClassNotFoundException, IllegalAccessException, InstantiationException {
            return (Dialect)Class.forName(domaDialect).newInstance();
        }

    }

}
  • protected static class DomaBeanConfig { ... } を追加します。DomaBeanConfig クラスには @Configuration を付加します。

MAIL001MailHelper.java

package ksbysample.webapp.email.helper.mail;

import ksbysample.webapp.email.config.Constant;
import ksbysample.webapp.email.util.VelocityUtils;
import ksbysample.webapp.email.web.mailsend.MailsendForm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

@Component
public class MAIL001MailHelper {

    private final String templateLocation = "mail/MAIL001/MAIL001-body.vm";

    @Autowired
    private VelocityUtils velocityUtils;

    public SimpleMailMessage createMessage(MailsendForm mailsendForm) {
        SimpleMailMessage mailMessage = new SimpleMailMessage();
        mailMessage.setFrom(mailsendForm.getFromAddr());
        mailMessage.setTo(mailsendForm.getToAddr());
        mailMessage.setSubject(mailsendForm.getSubject());
        mailMessage.setText(generateTextUsingVelocity(mailsendForm));
        return mailMessage;
    }

    private String generateTextUsingVelocity(MailsendForm mailsendForm) {
        Constant constant = Constant.getInstance();
        Map<String, Object> model = new HashMap<>();
        model.put("name", mailsendForm.getName());
        model.put("sex", constant.SEX_MAP.get(mailsendForm.getSex()));
        model.put("type", constant.TYPE_MAP.get(mailsendForm.getType()));

        String itemList = mailsendForm.getItem().stream()
                .map(constant.ITEM_MAP::get)
                .collect(Collectors.joining(", "));
        model.put("item", itemList);

        model.put("naiyo", mailsendForm.getNaiyo());
        return velocityUtils.merge(this.templateLocation, model);
    }

}
  • クラスに @Component アノテーションを付加します。
  • private String templateLocation;private final String templateLocation = "mail/MAIL001/MAIL001-body.vm"; へ変更します。
  • private VelocityUtils velocityUtils; を追加します。
  • build, setForm, setVelocityUtils, setTemplateLocation メソッドを削除します。
  • メソッド名を create → createMessage へ変更します。
  • createMessage メソッドの実装を上記の内容に変更します。
  • generateTextUsingVelocity メソッドを追加し、メール本文の生成処理をこのメソッドに移動します。

MailsendService.java

package ksbysample.webapp.email.web.mailsend;

import ksbysample.webapp.email.dao.EmailDao;
import ksbysample.webapp.email.dao.EmailItemDao;
import ksbysample.webapp.email.entity.Email;
import ksbysample.webapp.email.entity.EmailItem;
import ksbysample.webapp.email.helper.mail.MAIL001MailHelper;
import ksbysample.webapp.email.service.EmailService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.stereotype.Service;

@Service
public class MailsendService {

    @Autowired
    private EmailDao emailDao;

    @Autowired
    private EmailItemDao emailItemDao;

    @Autowired
    private MAIL001MailHelper mail001MailHelper;

    @Autowired
    private EmailService emailService;

    public void saveAndSendEmail(MailsendForm mailsendForm) {
        // 入力されたデータを email, email_item テーブルに保存する
        saveEmail(mailsendForm);

        // メールを送信する
        sendEmail(mailsendForm);
    }

    public void saveEmail(MailsendForm mailsendForm) {
        // email テーブルに保存する
        Email email = new Email();
        BeanUtils.copyProperties(mailsendForm, email);
        emailDao.insert(email);

        // email_item テーブルに保存する
        EmailItem emailItem = new EmailItem();
        for (String item : mailsendForm.getItem()) {
            emailItem.setEmailItemId(null);
            emailItem.setEmailId(email.getEmailId());
            emailItem.setItem(item);
            emailItemDao.insert(emailItem);
        }
    }

    public void sendEmail(MailsendForm mailsendForm) {
        SimpleMailMessage mailMessage = mail001MailHelper.createMessage(mailsendForm);
        emailService.sendSimpleMail(mailMessage);
    }

}
  • private VelocityUtils velocityUtils; を削除します。
  • private MAIL001MailHelper mail001MailHelper; を追加します。
  • sendEmail メソッドの実装を上記の内容に変更します。

build.gradle

buildscript {
    repositories {
        jcenter()
        // for org.springframework:springloaded
        maven { url "http://repo.spring.io/repo/" }
    }

    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.2.3.RELEASE")
        classpath("org.springframework:springloaded:1.2.3.RELEASE")
        // for Grgit
        classpath("org.ajoberstar:grgit:1.1.0")
    }
}

apply plugin: 'java'
apply plugin: 'spring-boot'
apply plugin: 'idea'

// for Doma 2
// JavaクラスとSQLファイルの出力先ディレクトリを同じにする
processResources.destinationDir = compileJava.destinationDir
// コンパイルより前にSQLファイルを出力先ディレクトリにコピーするために依存関係を逆転する
compileJava.dependsOn processResources

ext {
    git = org.ajoberstar.grgit.Grgit.open(file('.'))
}

jar {
    baseName = 'ksbysample-webapp-email'
    version = '0.0.1-SNAPSHOT'
}

idea {
    module {
        inheritOutputDirs = false
        outputDir = file("$buildDir/classes/main/")
    }
}

configurations {
    domaGenRuntime
}

repositories {
    jcenter()
    // for org.seasar.doma:doma
    maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
}

dependencies {
    def jdbcDriver = "org.postgresql:postgresql:9.4-1201-jdbc41"

    // spring-boot-gradle-plugin によりバージョン番号が自動で設定されるもの
    // Appendix E. Dependency versions ( http://docs.spring.io/spring-boot/docs/current/reference/html/appendix-dependency-versions.html ) 参照
    compile("org.springframework.boot:spring-boot-starter-web")
    compile("org.springframework.boot:spring-boot-starter-thymeleaf")
    compile("org.springframework.boot:spring-boot-starter-data-jpa")
    compile("org.springframework.boot:spring-boot-starter-velocity")
    compile("org.springframework.boot:spring-boot-starter-mail")
    compile("org.codehaus.janino:janino")
    testCompile("org.springframework.boot:spring-boot-starter-test")
    testCompile("org.yaml:snakeyaml")

    // spring-boot-gradle-plugin によりバージョン番号が自動で設定されないもの
    compile("${jdbcDriver}")
    compile("org.seasar.doma:doma:2.2.0")
    compile("org.bgee.log4jdbc-log4j2:log4jdbc-log4j2-jdbc4.1:1.16")
    compile("org.apache.commons:commons-lang3:3.4")
    compile("org.projectlombok:lombok:1.16.2")
    testCompile("org.dbunit:dbunit:2.5.0")

    // for Doma-Gen
    domaGenRuntime("org.seasar.doma:doma-gen:2.2.0")
    domaGenRuntime("${jdbcDriver}")
}

bootRun {
    jvmArgs = ['-Dspring.profiles.active=develop']
}

test {
    jvmArgs = ['-Dspring.profiles.active=unittest']
}

// for Doma-Gen
task gen << {
    def rootPackageName = 'ksbysample.webapp.email'

    ant.taskdef(resource: 'domagentask.properties',
            classpath: configurations.domaGenRuntime.asPath)
    ant.gen(url: 'jdbc:postgresql://localhost/ksbyemail', user: 'ksbyemail_user', password: 'xxxxxxxx') {
        entityConfig(packageName: "${rootPackageName}.entity", useListener: false)
        daoConfig(packageName: "${rootPackageName}.dao")
        sqlConfig()

        // 自動生成したファイルを git add する
        def grgit = org.ajoberstar.grgit.Grgit.open(dir: project.projectDir)
        grgit.add(patterns: ['.'])
    }
}
  • buildscript の dependencies の中に classpath("org.ajoberstar:grgit:1.1.0") を追加します。
  • task gen の最後に grgit で add する処理を追加します。

履歴

2015/05/13
初版発行。