はじめに
Java(Kotlin)アプリでExchange Onlineとメール送受信することになり、簡単に調べがついたメール送信をしてみた。
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("org.springframework.boot") version "2.1.6.RELEASE"
kotlin("jvm") version "1.3.41"
kotlin("plugin.spring") version "1.3.41"
}
repositories {
mavenCentral()
}
dependencies {
implementation(platform("org.springframework.boot:spring-boot-dependencies:2.1.6.RELEASE"))
implementation("org.springframework.boot:spring-boot-starter-mail")
implementation(kotlin("stdlib-jdk8"))
}
val compileKotlin: KotlinCompile by tasks
compileKotlin.kotlinOptions {
jvmTarget = "1.8"
}
val compileTestKotlin: KotlinCompile by tasks
compileTestKotlin.kotlinOptions {
jvmTarget = "1.8"
}
spring:
mail:
host: smtp.office365.com
port: 587
username: め〜るあどれす
password: ぱすわ〜ど
properties:
mail:
smtp:
starttls:
enable: true
import org.springframework.boot.CommandLineRunner
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.mail.SimpleMailMessage
import org.springframework.mail.javamail.JavaMailSender
@SpringBootApplication
class Application(val mailSender: JavaMailSender): CommandLineRunner {
override fun run(vararg args: String?) {
val mailMessage = SimpleMailMessage().apply {
setTo("宛先め〜るあどれす")
from = "め〜るあどれす"
subject = "けんめい"
text = "ほんぶん"
}
mailSender.send(mailMessage)
}
}
fun main( args: Array<String>) {
runApplication<Application>(*args)
}
- 今までMaven専門できたけど、GroovyじゃなくてKotlinで書けるようになったというのでGradleに挑戦中。だから記述内容が最適化は自信なし。これからもGradleを積極的に使うつもりだけど、時々Mavenより判りやすいという話を聞くけど、自由度が高いから当然判りづらいという感想。
- プロパティでmail.smtp.ssl.enable=trueだと、javax.net.ssl.SSLException: Unsupported or unrecognized SSL messageが発生するので、こちらを参考にした。mail.smtp.authはなくても、デバッグログ的に動きに差はなかった。
このように簡単にメール送信ができたので、Exchange Onlineとはいえ普通?のメールサーバと同じで受信も簡単にできるだろうと思ってしまった。最終的にはその通りなのだが、途中で躓くと疑心暗鬼にかられて右往左往してしまった。
ちょっと雲行きが...
メール送信できたから受信も当然できると言いふらしてしまい、いざ実装という段階になって、あれ!?と思ったのが、JavaMailSenderがあるspring-context-support-5.1.8.RELEASE.jarにJavaMailReciverはない。JavaMail自身は受信もできるのに何故?。spring integrationにはメール受信もあるけど、わざわざ組み込む気にはなれずJavaMailを直接使うことにした。
しかし、いろいろな実装をラッピングして使いやすくしてくれるSpring Frameworkにメール受信がないってことは、何か共通化しにくい事情があるのでは?と思ってしまった。
そしてハマった
JavaMailでの受信ロジックだけど、それほど警戒することはないものだった。
val props = Properties()
// セッションを生成
val session = Session.getInstance(props)
// プロトコルを選択
val store = session.getStore("imaps")
// サーバに接続
store.connect("outlook.office365.com", 993, "め〜るあどれす", "ぱすわ〜ど")
// フォルダを選択
val folder = store.getFolder("INBOX")
folder.open(Folder.READ_ONLY)
// メール受信
folder.messages.map {
println(it.subject)
}
folder.close(false)
store.close()
するとstore.connectメソッドの呼び出しでjavax.mail.AuthenticationFailedException: AUTHENTICATE failedが発生。session.debug = trueを指定して詳細なログを取得すると
DEBUG: setDebug: JavaMail version 1.6.2
DEBUG: getProvider() returning javax.mail.Provider[STORE,imaps,com.sun.mail.imap.IMAPSSLStore,Oracle]
DEBUG IMAPS: mail.imap.fetchsize: 16384
DEBUG IMAPS: mail.imap.ignorebodystructuresize: false
DEBUG IMAPS: mail.imap.statuscachetimeout: 1000
DEBUG IMAPS: mail.imap.appendbuffersize: -1
DEBUG IMAPS: mail.imap.minidletime: 10
DEBUG IMAPS: closeFoldersOnStoreFailure
DEBUG IMAPS: trying to connect to host "outlook.office365.com", port 993, isSSL true
* OK The Microsoft Exchange IMAP4 service is ready.
A0 CAPABILITY
* CAPABILITY IMAP4 IMAP4rev1 AUTH=PLAIN AUTH=XOAUTH2 SASL-IR UIDPLUS ID UNSELECT CHILDREN IDLE NAMESPACE LITERAL+
A0 OK CAPABILITY completed.
DEBUG IMAPS: AUTH: PLAIN
DEBUG IMAPS: AUTH: XOAUTH2
DEBUG IMAPS: protocolConnect login, host=outlook.office365.com, user=め〜るあどれす, password=<non-null>
DEBUG IMAPS: AUTHENTICATE PLAIN command trace suppressed
DEBUG IMAPS: AUTHENTICATE PLAIN command result: A1 NO AUTHENTICATE failed.
なんか認証に失敗している?ここが迷走の始まり。
プロトコルをpop3sに、ポート番号を995で試したけど
DEBUG: setDebug: JavaMail version 1.6.2
DEBUG: getProvider() returning javax.mail.Provider[STORE,pop3s,com.sun.mail.pop3.POP3SSLStore,Oracle]
DEBUG POP3: mail.pop3s.rsetbeforequit: false
DEBUG POP3: mail.pop3s.disabletop: false
DEBUG POP3: mail.pop3s.forgettopheaders: false
DEBUG POP3: mail.pop3s.cachewriteto: false
DEBUG POP3: mail.pop3s.filecache.enable: false
DEBUG POP3: mail.pop3s.keepmessagecontent: false
DEBUG POP3: mail.pop3s.starttls.enable: false
DEBUG POP3: mail.pop3s.starttls.required: false
DEBUG POP3: mail.pop3s.finalizecleanclose: false
DEBUG POP3: mail.pop3s.apop.enable: false
DEBUG POP3: mail.pop3s.disablecapa: false
DEBUG POP3: connecting to host "outlook.office365.com", port 995, isSSL true
+OK The Microsoft Exchange POP3 service is ready.
CAPA
+OK
TOP
UIDL
SASL PLAIN
USER
.
DEBUG POP3: authentication command trace suppressed
DEBUG POP3: authentication command failed
QUIT
+OK Microsoft Exchange Server POP3 server signing off.
やはり認証で失敗している。
そこでネットで情報を漁ると、メールアドレスではなくて@より前で認証しないとダメとか、それだとドメインが判らないだろうからoutlook.office365.comの先でプロキシ?かリダイレクト?されているExchangeサーバに直接接続しないとダメじゃないかとか、などなど半日程度試行錯誤を繰り返しました。
こたえ
Exchange管理コンソールでユーザのメール接続設定でIMAPやPOP3が無効になってました。
上記の直接Exchangeサーバに接続しなきゃって時に、管理者の人にお願いして管理コンソールにアクセスさせてもらいました。直接は結局TCP/IPが通らずダメでしたが、この時にみたドキュメントに「既定では、POP3 と IMAP4 は Exchange Online のすべてのユーザーに対して有効になっています。」だがユーザ毎に無効化できるってことで確認してみたら、まんまと無効化されてました。
そこで有効化して、しばらくすると(5~10分)、すんなり動きました!