Ba.とWEBと梅干し太郎

WEB制作・プログラミング・音楽の学習したこと・つくったもののアウトプットブログです。あとは日記。

(Java) Play FrameWork入門! Twitter のつぶやき検索アプリを作ってみたよ

どうも!もっこりmokkoです。

日付も変わろうとしていますが、Hamee Advent Calendar 2016の10日目の記事です!
初アドカレにドキがムネムネなわけですが、最初書こうと思っていた内容(スクレイピングと置換)は自分自身納得ができなかったのでまた次回に。

というわけで、今日はScalaフレームワークとして有名な「Play Framework」のJava版入門を1つ。
簡単なTwitterのエゴサアプリを作ってみました。

デモ
※herokuのFreeの使用時間超えて止まっちゃいました。(12月11日追記)


この記事から分かること
- PlayFrameWorkの導入方法
- 簡単なアプリケーションのつくりかた
- 開発環境 & 本番環境での動かし方
(組み込みのNetty と Tomacatの2手法を紹介)


Play FrameWorkとは

The High Velocity Web Framework For Java and Scala

公式ドキュメントより引用
https://www.playframework.com/

Ruby on Rails感覚で扱えるJavaフレームワーク
Scala界ではかなり有名だそう。

導入方法
公式ページよりactivatorというツールをインストール。
▼こちらからどぞ
Typesafe Activator(https://www.lightbend.com/activator/download)

コマンドラインを通して、コマンドを叩けばすぐ使える。

▼ちょっとサボるけど、詳しくはこちらを参照
Installing - 2.4.x

プロジェクトを作成するコマンドはこれ。

$ activator new 

対話式でアプリ名など色々聞かれるので答えると必要なファイルが生成される。

Javaアプリを作成する場合はplay-javaを選択

Browse the list of templates: http://lightbend.com/activator/templates
Choose from these featured templates or enter a template name:
  1) minimal-akka-java-seed
  2) minimal-akka-scala-seed
  3) minimal-java
  4) minimal-scala
  5) play-java
  6) play-scala
(hit tab to see a list of all templates)

簡単なアプリケーションを作ってみた
機能はtwitterでエゴサしてそれを引っ張ってくるだけ。

※Play Frameworkのバージョンは2.4.8を使用。現時点での最新バージョンは2.5.10。

▼画面はこんなの
f:id:masayannuu:20161210234432p:plain

コマンドでプロジェクトを作成すると既に Model 、View、Controllerのクラスが作成されている。
このMVCの仕様はjava初めての私でもとても分かりやすかった。

▼Modelクラスはこんな感じ。

package models;

public class SearchWord {

    public String word;

    public String getWord() {
        return word;
    }

    public void setWord(String word) {
        this.word = word;
    }
}

今回はPOSTされた文字列をtwitterでエゴサするだけなのでシンプルに。
DBに繋がないのでORMも使用しない。
Ebeanを使用した時はebeanのModelクラスを継承して使った。

▼そして、conf/routesにルーティングを書く。

#アプリのルーティングを設定するファイル  
#HTTPリクエスト  URL   呼び出すメソッド名 の順に記載  
GET     /                           controllers.ApplicationController.index()

POST    /search                     controllers.ApplicationController.search()

# 静的ファイルのルーティング viewの呼び出しに使用する
GET     /assets/*file               controllers.Assets.at(path="/public", file)

こんな感じで、アクセスしたURLに応じて呼び出すControllerを指定する。
分かりやすい。

▼Controllerクラス

package controllers;

import models.SearchWord;
import play.data.Form;
import play.mvc.Controller;
import play.mvc.Result;
import twitter4j.TwitterException;
import utils.TwitterSearch;

import java.util.List;
import java.util.Map;
public class ApplicationController extends Controller {

    /** POSTされた文字を元にtwitterでエゴサをかけるよ*/
    public Result search() throws TwitterException {
        Map<String, String> postData = new Form(SearchWord.class).bindFromRequest().data();
        List<twitter4j.Status> searchResult =  TwitterSearch.searchTweet(
                postData.get("searchWord"))
                .getTweets();

        return ok(views.html.listTweet.render(searchResult));
    }

    /** viewを表示するだけだよ */
    public Result index() {
        return ok(views.html.index.render());
    }

}

Resultクラスで受け取ってviewへレンダリング
POSTされた値を受け取る時はFormクラスを使えばMAP型で受け取れる。

twitterAPIを叩くライブラリはTwitter4Jを使わせて頂きます。

package utils;

import play.Play;
import twitter4j.*;
import twitter4j.conf.ConfigurationBuilder;

public class TwitterSearch {

    public static QueryResult searchTweet(String searchWord) throws TwitterException {

        ConfigurationBuilder configInfo = new ConfigurationBuilder();
        //application.confからTwitterの設定を読み込むよ
        configInfo.setDebugEnabled(true)
                .setOAuthConsumerKey(Play.application().configuration().getString("twitter.authKey"))
                .setOAuthConsumerSecret(Play.application().configuration().getString("twitter.authSecret"))
                .setOAuthAccessToken(Play.application().configuration().getString("twitter.authToken"))
                .setOAuthAccessTokenSecret(Play.application().configuration().getString("twitter.authSecretToken"));
        TwitterFactory tf = new TwitterFactory(configInfo.build());

        Twitter twitter = tf.getInstance();
        Query query = new Query();
        query.setQuery(searchWord);
        QueryResult result = twitter.search(query);

        return result;

    }

}

twitterの認証に使う情報はconf/application.confという設定ファイルに外出し。
このapplication.confという設定ファイルは環境変数から読み込めるのでおすすめ。

#twitterの認証情報を環境変数から取得  
twitter.authKey=${?AUTH_KEY}
twitter.authSecret=${?AUTH_SECRET}
twitter.authToken=${?AUTH_AUTHTOKEN}
twitter.authSecretToken=${?AUTH_SECRET_TOKEN}

#アプリの言語
play.i18n.langs = [ "ja" ]
 

▼他クラスでapplication.confの変数を呼ぶ時はこれ
(※Playのバージョンによって使うクラスが絶妙に違う。2.4はこれでいける)

Play.application().configuration().getString("twitter.authKey")

▼Viewクラス

@import b3.vertical.fieldConstructor

@main("ツイッターくん") {
    @b3.form(routes.ApplicationController.search, 'class -> "form-group") {
           <div class="col-sm-offset-0 col-sm-6">
                <label for="searchWord">#つぶやき検索</label>
                <input type="text" name="searchWord">
                @b3.submit('class -> "btn btn-default"){ <span class="glyphicon glyphicon-globe"></span> 検索}
           </div>
    }
}

Play Framewokのviewテンプレート。
ヘルパーでhtml生成はもちろん、素書きでもOKなので扱いやすい。
bootstrapにも対応しており、公式のPlay Bootstrapがあるのであまり手をかけず整ったページを作れる。

viewテンプレートの記法はScalaを使う。
@以降がScalaとして認識されてるのでせ、ループ処理もこのように書ける。

@(searchResult: List[twitter4j.Status])

@main("ツイッターくん") {
    @for(tweet <- searchResult) {
        <li>@tweet.getText()</li>
    }
}

分かりづらいのが、@main()なるもの。
これは要するに変数で、viewの共通ファイルになっているmain.scala.htmlというファイルに渡すhtml要素を定義している。

▼main.scala.htmlはこんな感じ
最上部に@()で定義されているのが、このファイルに渡ってくる引数。
他のviewの@main("ツイッターくん"){ hogehoge }の"ツイッターくん"がmain.scala.htmlのtitleとして、そして{hogehoge}がhtmlとして渡されている。

@(title: String)(content: Html)

<!DOCTYPE html>
<html>
    <head>
        <title>@title</title>
        <link href="//netdna.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet">
        <link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/main.css")">
        <link rel="shortcut icon" type="image/png" href="@routes.Assets.at("images/favicon.png")">
        <script src="@routes.Assets.at("javascripts/jquery-1.9.0.min.js")" type="text/javascript"></script>
    </head>
    <body>
        <div class="page-header">
            <a class="navbar-brand logo" href="/">
                <img src="@routes.Assets.at("images/medjed.png")" />
            </a>
            <h1>Play Frameworkのテストだよ
                <br>
                <small>つぶやきを検索するよ</small>
            </h1>
        </div>

        @content

    </body>
</html>

ささ、これだけでつぶやき取得アプリが完成!
テストと入力すると... f:id:masayannuu:20161211002329p:plain

つぶやきを取得する。
f:id:masayannuu:20161211002426p:plain

デプロイしてみる
組み込みサーバーで動かすのはactivatorのコマンドで

$ activator start

application.confでsecret key(クライアントセッションの暗号化に使う)を指定していない場合はstartの引数に

$activator start -Dapplication.secret=abcdefghijk

だけでOK。

▼組み込みサーバーでデプロイ時のコマンドについて。こちら参照
Production - 2.4.x

Tomcatでデプロイする場合はwarファイルを作成する必要があるので、 play2warというプラグインを入れてコマンド1つでサクッと。

project/plugin.sbtに

addSbtPlugin("com.github.play2war" % "play2-war-plugin" % "1.4-beta1")

と build.sbtに

import com.github.play2war.plugin._  

libraryDependencies ++= Seq(
  cache,
  jdbc,
  "mysql" % "mysql-connector-java" % "5.1.40",
  "com.adrianhurt" %% "play-bootstrap" % "1.1-P24-B3",
  "org.twitter4j" % "twitter4j-core" % "4.0.5"
)

このように依存関係を追加すればコマンドからwarファイルを作成できる。

コマンドは

$activator war

使っているplay frameworkのバージョンによってバージョンの指定が異なるので注意! 2016年12月時点で2.5以降は非対応。

後はこのwarファイルをTomcat直下のwebapps内に置いてTomcatを立ち上げればデプロイ完了。


使ってみた感想
java初心者にとってとっつきやすかった。
ハマりどころも多いが嫌いじゃないし、むしろ好きかも。

PS.
▼ライブラリ探しはmaven repositoryでどうぞ。
https://mvnrepository.com/