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

Challenge Java EE !

Java EEを中心に趣味や仕事における開発メモを書いています。

Spring BootとLINE Messaging APIで作ったLINE BOTをHerokuで動かす

Spring Boot LINE Messaging API Heroku

やっと触れましたLINE Messaging API。やばい、なにこれめっちゃ楽しい。

上のは完全に遊びですが、仕事でも真面目に検討してもいいのかもって今更ながら真面目に思い始めました。QRコードから簡単に友だち追加できるので、何かと初速が速いなぁと。とりあえずスマホだけあれば良いわけですし。と当たり前なことを今更言ってしまうぐらい楽しいです。

Javaな方々は公式からThe Messaging API SDK for Javaが出てるので、これで簡単に始められます。

github.com

ドキュメントは以下参照。

LINE API Reference

line-bot-spring-boot moduleもあるのでSpring Bootの勉強がてら始めるのもホントおすすめです(自分は完全にそんな感じ)

GitHubのページにサンプルコードありますが、単純に送ったメッセージをやまびこのようにブーメランするだけなら

package com.example.bot.spring.echo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import com.linecorp.bot.model.event.Event;
import com.linecorp.bot.model.event.MessageEvent;
import com.linecorp.bot.model.event.message.TextMessageContent;
import com.linecorp.bot.model.message.TextMessage;
import com.linecorp.bot.spring.boot.annotation.EventMapping;
import com.linecorp.bot.spring.boot.annotation.LineMessageHandler;

@SpringBootApplication
@LineMessageHandler
public class EchoApplication {
    public static void main(String[] args) {
        SpringApplication.run(EchoApplication.class, args);
    }

    @EventMapping
    public TextMessage handleTextMessageEvent(MessageEvent<TextMessageContent> event) {
        System.out.println("event: " + event);
        return new TextMessage(event.getMessage().getText());
    }

    @EventMapping
    public void handleDefaultMessageEvent(Event event) {
        System.out.println("event: " + event);
    }
}

で作れます。便利すぎる(^^;

誰でも簡単に、って感じなのだと思いますが、知ってると便利かなと思うことをまとめたいと思います。

クラウドとか使わずローカル環境で動作確認したいとき

当初、なんかサーバとかをクラウドに用意せんと動かせないよなーとか思ってたのですが、去年JJUG CCC 2016 FallでLINEの@tokuhiromさんがLINE BOT ライブコーディングをされていて、ngrokを使う方法を紹介されてました。

Java + spring-boot で書く! LINE BOT ライブコーディング。 · Issue #34 · jjug-ccc/call-for-paper-2016fall · GitHub

ngrokは知っていたので、これ聴いたとき、ぬおおー!その手があったのかって感じでした。(知ってたなら気づけっていう

ngrok.com

ngrok http 8080

と起動すればローカルPCの指定ポートを外部にトンネルしてくれます。

f:id:kikutaro777:20170116222625p:plain

Spring Bootでアプリを起動しておけば、上記ngrokでトンネルされたhttpとhttpsのURLをpublicに使うことできますので、httpsのアドレスをLINE Messaging APIに登録することができます。

f:id:kikutaro777:20170116222900p:plain

httpは以下のように登録できないので、ngrok使ったやり方は非常にお手軽だと思います。証明書もいらないし。

f:id:kikutaro777:20170116222942p:plain

設定まわり URLにcallback

Webhookを登録したとき、Verifyがうまくいかない、と最初ちょっとハマってました。Spring Boot利用したとき、デフォルトでは先ほどのキャプチャにあるように「https://hogehoge.ngrok.io/callback」のような形で「callback」と入れてあげる必要があります。GitHubのConfigurationに、デフォルトが「callback」になっていることが書いてあるのですが最初気づかなくて、ハマってました。

f:id:kikutaro777:20170116224349p:plain

あくまでデフォなので、変えてしまうのもありです。

設定まわり 自動応答メッセージ

ちゃんとドキュメント読めって感じなのですが、最初、WebhookのVerifyが成功してローカルPCにもデータ飛ぶのに、LINEから話かけると常に以下の「メッセージありがとうございます 申し訳ありませんが、このアカウントでは個別のご返信ができないのです 次回の配信をお楽しみに」と出てしまって、わからんーってなってました(^^;

f:id:kikutaro777:20170116223626j:plain

LINE@Managerで「アカウント設定」->「Bot設定」の「詳細設定」->「自動応答メッセージ」を「利用しない」にするだけです。

f:id:kikutaro777:20170116223907p:plain

herokuでhttps

ここまででローカル開発環境で色々遊べるのですが、最後はやっぱり公開してみたい、ということがあると思います。証明書は安いものを取るか、Let's Encryptなど使うか、など選択肢があるかと思いますが、それなりに手間だなぁと。で、探してたらHerokuのAdd-onでFixieを使うと簡単とのこと。

qiita.com

実際に試したらホント簡単でした(^^;

画像や動画、スタンプの認識

最後におまけですが、冒頭に出したサンプルコードはテキストメッセージをやまびこするものでしたが、画像とか動画、スタンプが送られてきた場合、どうフックするのかなーと思ったのですが、@EventMapping使って以下のように定義すれば、そのままフックされました。

@EventMapping
public Message handleStickerMessage(MessageEvent<StickerMessageContent> event) {
    return new TextMessage("スタンプ送信ありがとうございます!");
}
    
@EventMapping
public Message handleImageMessage(MessageEvent<ImageMessageContent> event) {
    return new TextMessage("画像送信ありがとうございます!");
}
    
@EventMapping
public Message handleVideoMessage(MessageEvent<VideoMessageContent> event) {
    return new TextMessage("動画送信ありがとうございます!");
}
    
@EventMapping
public Message handleAudioMessage(MessageEvent<AudioMessageContent> event) {
    return new TextMessage("音声送信ありがとうございます!");
}

@EventMapping
public Message handleFollowEvent(FollowEvent event) {
    return new TextMessage("友達追加ありがとうございます!");
}

Herokuの環境変数利用

LINEのChannelSecretやTokenはコードに埋め込んだり、設定ファイルでGitHubなどにアップするわけにはいかないので、Herokuなどでは環境変数を利用するのが良いかと思います。で、gradleを利用してる場合は

sample-spring-boot-echoのREADMEにあるように
github.com

../gradlew bootRun -Dline.bot.channelToken=YOUR_CHANNEL_TOKEN \
                   -Dline.bot.channelSecret=YOUR_CHANNEL_SECRET

みたいに指定するようです。自分はMaven使ってたのと、application.propertiesを上手く使えないかなぁと思って調べたら

line.bot.channelSecret = ${CHANNEL_SECRET}
line.bot.channelToken = ${CHANNEL_TOKEN}

というように定義することができました。ほえー便利。

ということで、Spring Bootの勉強を楽しみながらするのにもおススメなLINE Messaging APIでした!

すごい面白いのができたらAwardsに出して1000万狙うしかない!(無理

botawards.line.me

2016年に最も読まれた記事 TOP10 Java EE少ない…

雑記

2016年で分析してみました。ページビューの総数は317,337でした。

f:id:kikutaro777:20170114191333p:plain

ちなみにブログの総アクセス数は2016年で100万アクセスを超えました(^^)!(逆にこわい)

ということで2016年のTop10

1位 アクセス数 11,601

なぜこれが1位!?びっくりです。
kikutaro777.hatenablog.com

2位 アクセス数 11,174

はじめてAdvent Calendarに参加した記事で想い入れが強い記事です。ちょくちょく昔のPrimeFaces記事に質問もらったりするので、わりと使われてるのかなー。
kikutaro777.hatenablog.com

3位 アクセス数 10,217

この辺は懐かしくもあり、未だにちゃんとわかってない気がするところ(^^;
kikutaro777.hatenablog.com

4位 アクセス数 9,916

小ネタ系が多い(^^;
kikutaro777.hatenablog.com

5位 アクセス数 7,189

キター!Twitter4J!長期的にみるとこれが一番アクセス多いんですよね(^^;ホント便利。@yusukeさんありがとうございます!
kikutaro777.hatenablog.com

と、ここでちょっと番外編として、はてブが多くついた記事を振り返ってみます。が、2016年は2つしかないwww 悲しい(>_<;けど書いてる量も質問下がりまくりなので仕方がない。(常駐してて全然書けねー時期モゴモゴ

番外編

はてブ1位

間違いなく2016年で一番感動したプレゼン。噂では聴いていたけど生でみてびっくりしました。気が付けばエバンジェリストスクールを毎週欠かさず聴いて、日々刺激をもらってます。
kikutaro777.hatenablog.com

はてブ2位

2位にあげるのも…ってレベルですが(^^;そして、どちらもJava EEじゃないっていうwww
kikutaro777.hatenablog.com

番外編 その2

2016年は執筆に参加させていただいたパーフェクト Java EEが発売にもなった年でした。JavaOneでまたJava EE少し盛り上がらないかなぁと期待していましたが、うーん(^^;;本は売れているのだろうか(汗
でもホント素晴らしい方々と書かせていただいて、良い経験になりました。恐縮しまくり…。

kikutaro777.hatenablog.com

ではでは戻ります。

6位 アクセス数 6,874

やっとこガチな感じのJSFネタ!でももうJSFは時代じゃない気も。
kikutaro777.hatenablog.com

7位 アクセス数 6,466

Azureこれはもうダッシュボードとか含めて古い情報になってしまってますね(^^;今年はまたAzureも触っていきたいです。
kikutaro777.hatenablog.com

8位 アクセス数 5,136

6位に続いて、こちらもJSFネタ。現場からお届けしてた感じのやつです。
kikutaro777.hatenablog.com

9位 アクセス数 4,669

Active Directoryかー、わりと読まれるんだなぁ(^^;
kikutaro777.hatenablog.com

10位 アクセス数 4,183

最後もJSFですか。JPAは全然入ってないんだな…。
kikutaro777.hatenablog.com

ということでこんな感じで、Java EEネタは3,4割くらいですかね(^^;;最近はQiitaにも書いてるのでご興味あれば是非フォローお願いしますm(_ _)m

qiita.com

NetBeansでSpring Boot + Doma

NetBeans Spring Spring Boot Doma

最近、はじめてDomaを触りました。前々からあちこちで「Doma、いいよ!Doma!」と聞いていて存在は知っていたのですが。

私は長らくJava EEをやってたので、IDEはNetBeansが馴染んでいて、他のフレームワークを触るときでも、ついNetBeansを使いたくなります。で、Domaを調べていると、EclipseとIntelliJ IDEAの情報がほとんどでNetBeans関連が見当たりませんでした。

Spring Bootと組み合わせて使いたいなと思って、調べたところ@makingさんが作ったdoma-spring-boot-starterが出てきて、ここのGitHubのページがIntelliJ IDEAを使った丁寧なチュートリアルになっていました。

github.com

で、このチュートリアルを参考にNetBeansで少し試してみたら、SQLファイルを配置したとき以下のようなコンパイルエラーがでました。

-------------------------------------------------------------
COMPILATION ERROR : 
-------------------------------------------------------------
jp/co/kke/doma/dao/MagazineDao.java:[18,20] 
[DOMA4019] ファイル[META-INF/jp/co/kke/doma/dao/MagazineDao/
selectAll.sql]がクラスパスから見つかりませんでした。
ファイルの絶対パスは
"C:\Users\kikuta\Documents\NetBeansProjects
\SpringBootDomaSample\target\classes\META-INF\jp\co\kke
\doma\dao\MagazineDao\selectAll.sql"です。
1 error
-------------------------------------------------------------
-------------------------------------------------------------
BUILD FAILURE
-------------------------------------------------------------

Domaではapt(Annotation Processing Tool)によってソースを自動生成してくれるようなのですが、sqlファイルをみつけられないようです。コードも以下のようにエラーとなっています。

f:id:kikutaro777:20161228000545p:plain

そこで、maven-apt-pluginで以下を追加しました。

<plugin>
    <groupId>com.mysema.maven</groupId>
    <artifactId>maven-apt-plugin</artifactId>
    <version>1.0.4</version>
    <executions>
        <execution>
            <goals>
                <goal>process</goal>
            </goals>
            <configuration>
                <outputDirectory>src/main/resources/META-INF/jp/co/kke/doma/dao/MagazineDao</outputDirectory>
                <processor>org.seasar.doma.boot.ConfigAutowireable</processor>
            </configuration>
        </execution>
    </executions>
</plugin>

これでビルドが通って、Domaが動かせました。

Java EEやってる人はNetBeansに慣れている人が多いと思いますが、自分みたいに「最近SpringBootが気になるので、Spring Boot + Domaやってみたいなぁ、できればNetBeansで」と思ってる人がいれば、以下試した手順が参考になれば幸いです。

環境

自分の試した環境は

  • Windows 10 Pro 64bit
  • JDK 8u112 64bit
  • NetBeans 8.2

です。

NB Spring Boot

Spring Bootにはプロジェクトのひな型を作ってくれるSpring Initializrがあります。Webで作ってもいいのですが、NetBeansから使えると楽です。そこで、NB Spring Bootというプラグインを利用します。

「ツール」の「プラグイン」メニューを選択します。
f:id:kikutaro777:20161228001639p:plain

NB Spring Bootにチェックを入れてインストールします。
f:id:kikutaro777:20161228001955p:plain

f:id:kikutaro777:20161228002038p:plain

f:id:kikutaro777:20161228002126p:plain

Spring Bootのプロジェクト作成

NB Spring Bootプラグインを入れた後、「ファイル」「新規プロジェクト」の「Maven」から「Spring Boot Initializr project」を選びます。
f:id:kikutaro777:20161228004421p:plain

プロジェクト情報を入れます。
f:id:kikutaro777:20161228004432p:plain

RESTを使ってDBの値を取得するため、Webにチェックを入れます(ここではLombokも選びました)。
f:id:kikutaro777:20161228004542p:plain

DBはPostgres(Heroku上の)を使うのでチェックし、JDBCもチェックしました。
f:id:kikutaro777:20161228004551p:plain

f:id:kikutaro777:20161228004558p:plain

ちなみに今回サンプルで作ったDBは以下簡単なcreate文で作ったテーブルです。

create table magazine
(
    id serial not null,
    mail text
)

Domaの利用

プロジェクトができたら、pom.xmlのdependenciesに以下追加します。

<dependency>
    <groupId>org.seasar.doma.boot</groupId>
    <artifactId>doma-spring-boot-starter</artifactId>
    <version>1.1.0</version>
</dependency>

application.propertiesファイルにDB接続情報を入れます。

doma.dialect = postgres
spring.datasource.initialize=true
spring.datasource.url=	jdbc:postgresql://hogehoge?ssl=true&sslfactory=org.postgresql.ssl.NonValidatingFactory
spring.datasource.username=hogehoge
spring.datasource.password=hogehoge
spring.datasource.driverClassName=org.postgresql.Driver

コードは、Entityクラス、DAO、ServiceとControllerをそれぞれ追加しました。

Entityクラス「Magazine.java」

import org.seasar.doma.Entity;
import org.seasar.doma.GeneratedValue;
import org.seasar.doma.GenerationType;
import org.seasar.doma.Id;

@Entity
public class Magazine {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public Integer id;
    public String mail;
}

DAO

import jp.co.kke.doma.entity.Magazine;
import java.util.List;
import org.seasar.doma.Dao;
import org.seasar.doma.Select;
import org.seasar.doma.boot.ConfigAutowireable;

@ConfigAutowireable
@Dao
public interface MagazineDao {
    @Select
    List<Magazine> selectAll();
}

DAOに合わせてDoma用のSQLファイル「selectAll.sql」を作成し

src/main/resources/META-INF/jp/co/kke/spring/dao/MagazineDao/

に配置しました。

sqlファイルの中は

SELECT id, mail FROM magazine

と単純に全取得するものです。

続けてServiceクラスを作ります。

import jp.co.kke.doma.entity.Magazine;
import java.util.List;
import jp.co.kke.doma.dao.MagazineDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class MagazineService {
    @Autowired
    private MagazineDao magazineDao;
    
    public List<Magazine> getMagazines() {
        return magazineDao.selectAll();
    }
}

最後にRest用のコントローラを追加します。

import java.util.List;
import jp.co.kke.doma.entity.Magazine;
import jp.co.kke.doma.service.MagazineService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MagazineController {
    @Autowired
    private MagazineService magazineService;
    
    @GetMapping("/magazines")
    public List<Magazine> getMagazines() {
        return magazineService.getMagazines();
    }
}

NetBeansのキャプチャはこんな感じです。

f:id:kikutaro777:20161228005912p:plain

MagazineDaoは残念ながら赤いエラーが出てしまうのですが、実際にはコンパイルできます。

で、実行して「http://localhost:8080/magazines」へアクセスすると

f:id:kikutaro777:20161228011506p:plain

とDBの中身が表示されました!

ということで、NetBeansでしばらく触っていこうと思います。