Works by

Ren's blog

@rennnosuke_rk 技術ブログです

【Kotlin/Spring】Kotlin版SpringBoot開発環境を構築する

f:id:rennnosukesann:20190106130012p:plain:w300    f:id:rennnosukesann:20181220183020p:plain:w300

Kotlin版Spring Boot開発環境構築のメモです。

検証環境はMac OS X Mojave 10.14.2 になります。

IntelliJ IDEのインストール

今回はIDEとしてIntelliJを使用します。 IntelliJはKotlinの開発元であるJetbrain社が開発していることもあって、Kotlinのサポートはバッチリです。

下記リンクより遷移後、DownloadボタンからIntelliJをダウンロードできます。

www.jetbrains.com

プロジェクトの作成

次に、Spring InitializerからSpringプロジェクトの雛形を取得します。

取得するプロジェクトに関して、いくつかオプションを選択していきます。

f:id:rennnosukesann:20190109194245p:plain

まずビルドツールとして Maven Gradle のうち何れかを選択します。今回は Gradle のプロジェクトを作成していきます。

f:id:rennnosukesann:20190109194422p:plain

次に言語選定です。主要なJVM言語 Java Gloovy Kotlin から選択できます。 今回はKotlinで書くので「Kotlin」 を選択。

f:id:rennnosukesann:20190109194503p:plain

次にSpringのバージョンを選択します。今回は安定版の 2.1.1 を選択します。

f:id:rennnosukesann:20190109194630p:plain

プロジェクトのメタ情報について設定します。

GroupはGradleのGroupIDとなり、プロジェクトを一意に識別するためのものです。プロジェクトのソースファイルを含めるパッケージと同一にすることが慣習となっています。

ArtifactIDはいわゆるプロジェクト名です。

メタ情報に関する記述は下記参照。

maven.apache.org

f:id:rennnosukesann:20190109194810p:plain

最後にSpringに追加したいライブラリを入力・選択します。

今回はWeb(Webページ生成/APIインタフェース等のWeb関連機能)、JPA(永続化機能)、Security(認証機能)を選択しました。

f:id:rennnosukesann:20190109195844p:plain

最後に「Cenerate Project」ボタンを押して、zipファイルをダウンロードしてください。

f:id:rennnosukesann:20190109195925p:plain

プロジェクトを開く

次は、ダウンロードしたプロジェクトをIntelliJで開きます。

ダウンロードした圧縮済みプロジェクトを解凍し、解凍後のプロジェクトを任意のディレクトリに配置してください。 その後、ダウンロード(+インストール)したIntelliJを起動し、起動後ダイアログ中の「Open」をクリックしてください。

f:id:rennnosukesann:20190109201202p:plain

するとディレクトリ選択のためのファイルダイアログが開かれます。 ここでIntelliJで開きたいプロジェクトを選択。

f:id:rennnosukesann:20190109201327p:plain

「OK」をクリック。

f:id:rennnosukesann:20190109201523p:plain

これでプロジェクトがIntelliJ上に読み込まれました。

f:id:rennnosukesann:20190109202133p:plain

あとは開発するのみ!

ローカルでSpringアプリケーションを起動

実際にアプリケーションをローカルで起動してみます。

起動の前に、 build.gradle を少し編集します。 Spring Data JPAライブラリを読み込むとDBの設定が必要になるため、 今回は一旦外しておきます。

f:id:rennnosukesann:20190109234614p:plain

「View」->「Tool Windows」->「Gradle」 を選択。

f:id:rennnosukesann:20190109234745p:plain

「(プロジェクト名)」-> 「Tasks」->「application」->「bootRun」を右クリックし、「Run '(プロジェクト名)[bootRun]'」 を選択。

f:id:rennnosukesann:20190109234812p:plain

するとビルド・アプリケーションの起動が始まります。

23:39:41: Executing task 'bootRun'...

:compileKotlin
w: ... ated
:compileJava NO-SOURCE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:bootRun

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.1.1.RELEASE)

...

2019-01-09 23:40:22.533  INFO 31168 --- [           main] c.r.n.rpg.rpgkit.RpgKitApplicationKt     : Started RpgKitApplicationKt in 18.3 seconds (JVM running for 19.039)

アプリケーションの起動はこれで成功です!

参考文献

https://start.spring.io/

maven.apache.org

【Docker】イメージビルド時にキャッシュを使用しない

f:id:rennnosukesann:20190108224132p:plain

メモ。

Dockerイメージ作成時、デフォルトだと過去のビルドキャッシュが存在する場合そちらを使用してしまいます。

$ ls
Dockerfile
$ docker build -t hoge .
Step 2/20 : COPY entrypoint.sh /sbin/entrypoint.sh
 ---> Using cache
 ---> 5214f6bedefd

これにより、コマンドの実行結果やファイル内容の反映が上手くいかない場合があります。 --no-cache を指定することで、キャッシュが存在する場合も無視してビルドを実行できます。

$ docker build -t hoge . --no-cache
 ---> 5d870d6990ba
Step 2/20 : COPY entrypoint.sh /sbin/entrypoint.sh

以上です。

【Kotlin】Java(8) -> Kotlinやってみて「良い」と思ったことリスト

f:id:rennnosukesann:20190106130012p:plain:w500

Kotlinをやっていて、Java(8)と比較して良いな、と思った部分を書きました。

null許容型

Swiftなどにもありますが、Kotlinではnull許容型が定義されており、それと対を成すようにデフォルトの型宣言ではnullが許容されないようになっています。

val hoge: String = null // Error:(9, 23) Null can not be a value of a non-null type String
val hoge: String? = null // OK 

エルビス演算子(?:)

オペランド(被演算子)がnullのときのデフォルト値を設定できる

String fuga = hoge == null ? "hoge is null" : hoge;

val fuga = hoge ?: "hoge is null"

第一級関数

Kotlinは関数を言語仕様に持ち、かつそれらを第一級関数として扱えます(値として扱える)。

fun hoge(arg1: String) {
    println(arg1)
}

fun main(args: Array<String>) {
    val func = ::hoge // Javaのメソッド参照のように渡す
    func("hoge")
}

if式

Scalapythonのように、ifが式なので返り値を持ちます。

val isHoge = true;
val hoge = if (isHoge) "hoge" else "not hoge"

文字列テンプレート

JavaScript言語仕様が持つ文字列テンプレートをKotlinでも使用できます。 文字列リテラル内で $ を使って変数を文字列として埋め込みます。

val message = "Hello, Kotlin!"
println("Welcome to Kotlin world : $message")

${} を使用すると、カッコ内に式を使用できます。

val kotlin = "Kotlin!"
val java = "Java"
val isKotlin = true
println("Hello ${if (isKotlin) kotlin else java}")

クラスフィールドにデフォルトでアクセサがつく

immutableフィールド val で宣言した場合はgetterのみが、 mutableフィールド var で宣言した場合はsetter+getterが生成されます。 Kotlinでは暗黙的にgetter/setterが呼ばれます。

class Gorilla(
    val name: String,
    var isAdult: Boolean // getter + setter
)


fun main(args: Array<String>) {
    val gorilla = Gorilla("ゴリラ", true)
    
    // 暗黙的なgetter呼び出し
    println(gorilla.name)  // ゴリラ
    println(gorilla.isAdult) // true
    
    // 暗黙的なsetter呼び出し
    gorilla.isAdult = false
    println(gorilla.isAdult) // false
}

値forループの廃止

言語仕様で for (int i = 0; i < 10; i++) のような繰り返し変数をインクリメントしていく for 文が廃止されており、基本的に拡張forループのみを使用することになります。

Kotlinでfor文による値のインクリメントは .. 演算子によるレンジによって実現できます。

for (i in 1..10) {
    println(i) // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
}

レンジは逆順・数値飛ばしが可能です。

for(i in 10 downTo 1) {
    println(i) // 10, 9, 8, 7, 6, 5, 4, 3, 2, 1
}
for(i in 1..10 step 2) {
    println(i) // 1, 3, 5, 7, 9
}

レンジは閉区間であり、.. 演算子の右側の値を数列に含みます。 開区間のレンジを作る場合、 until を利用します。

for(i in 1 until 10) {
    println(i)  // 1, 2, 3, 4, 5, 6, 7, 8, 9
}

分割代入

オブジェクトプロパティを複数の変数に代入することができます。

fun divMod(a: Int, b: Int) : Pair<Int, Int> {
    return Pair(a / b, a % b)
}

fun main(args: Array<String>) {
    val (div, mod) = divMod(10, 3)
    println("div: $div , mod: $mod")
}

when式

Javaではif/switch文で処理できた複数の分岐処理が、Kotlinではwhen式と呼ばれる言語仕様で実装できます。 when式は式として評価されるので分岐先で結果を返す・変数に入れるなどの操作ができます。 whenはJavaのswitch文のように並列に記述できます。

fun main(args: Array<String>) {
    val num = 3;
    println(fizzbuzz123(num)) // 
}

fun fizzbuzz123(num: Int): String{
    return when (num) {
        1 -> "fizzbuzz"
        2 -> "fizz"
        3 -> "buzz"
        else -> "$num"
    }
}

また引数を省略することで、値マッチングの代わりに条件式による分岐が可能になります。

fun main(args: Array<String>) {
    val num = 3;
    println(fizzbuzz(num))
}

fun fizzbuzz(num: Int): String{
    return when {
        num % 15 == 0 -> "fizzbuzz"
        num % 3 == 0 -> "fizz"
        num % 5 == 0 -> "buzz"
        else -> "$num"
    }
}

名前付き引数/デフォルト引数

名前付き引数・デフォルト引数もサポートされています。

fun add(a: Int, b: Int = 1) :Int {
    return a + b;
}

fun main(args: Array<String>) {
    println(add(1,2)) //3
    println(add(1)) //2
    println(add(a = 4,b = 2)) // 6
}

全般的に他言語で実装済みのモダンで使いやすい言語仕様が入っている印象でした。 まだまだ良いところがありそうなので、追記するかもしれません。

【Kotlin】Kotlin CLIでKotlinをJavaバイトコードにコンパイル/実行する

f:id:rennnosukesann:20190106130012p:plain:w500

今日はじめてKotlinを触ってみたのですが、その際に使用したKotlin CLIによるコンパイルと実行のメモです。

Kotlin CLIのインストール

実施環境:Mac OS X Mojave 10. 14.2

brew でインストールできます。

$ brew update
$ brew install kotlin

コンパイル

適当な .kt ファイルを用意します。

hoge.kt
fun main(args: Array<String>) {
    println(args)
}

kotlinc コマンドでコンパイルします(オプションでKotlinランタイムを含めたjarを生成)。

$ kotlinc hoge.kt -include-runtime -d hoge.jar

jar ファイルが生成されるので、Java同様 java コマンドで実行します。

$ ls
hoge.kt hoge.jar
$ java -jar hoge.jar
[Ljava.lang.String;@1b6d3586

コンパイル時に -include-runtime でKotlinランタイムを含めているのは、Kotlinコンパイラコンパイルされて生成されたバイトコードがKotlinランタイムのライブラリに依存しているためです。ランタイムを含めずにそのままコンパイルすると、実行時に kotlin/jvm/internal/Intrinsics クラスが見つからず ClassNotFoundException 例外を送出します。

$ kotlinc hoge.kt
$ kotlinc _hoge.class
Exception in thread "main" java.lang.NoClassDefFoundError: kotlin/jvm/internal/Intrinsics
    at _hoge.main(1_5_1_compile.kt)
Caused by: java.lang.ClassNotFoundException: kotlin.jvm.internal.Intrinsics
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 1 more

ちなみに kotlinc コマンドを引数なしで実行すると、インタラクティブシェル(REPL)が起動されます。

$ kotlinc
Welcome to Kotlin version 1.3.11 (JRE 1.8.0_131-b11)
Type :help for help, :quit for quit
>>> println("Hello, Kotlin CLI !")
Hello, Kotlin CLI !
>>> :help 
Available commands:
:help                   show this help
:quit                   exit the interpreter
:dump bytecode          dump classes to terminal
:load <file>            load script from specified file

参考文献

kotlinlang.org

【Twitter】Twitter OAuthでメールアドレスの第三者共有を許可する設定

f:id:rennnosukesann:20190105182527p:plain:w300

メモ。

Twitter OAuth1.0a認証にて、メールアドレス認可を第三者に行いたい場合、下記のような設定が必要になります。

1. OAuth認証ページURLリクエストに include_email を含める

https://api.twitter.com/1.1/account/verify_credentials.json?include_email=true

2. Developerアカウントダッシュボード App detailsTerms of ServiceURL Privacy policy URL を設定する

f:id:rennnosukesann:20190105182045p:plain

f:id:rennnosukesann:20190105182119p:plain

3. Developerアカウントダッシュボード PermissionAdditional permissionsRequest email address from users を有効にする

f:id:rennnosukesann:20190105182301p:plain

f:id:rennnosukesann:20190105182313p:plain


以上です。

【npm/yarn】パッケージをダウングレードする

f:id:rennnosukesann:20181223181823p:plain:w500

メモ。

npm では、パッケージのダウングレードを行う場合、一旦パッケージを削除してから再度インストールし直す必要があります。

$ npm remove cowsay
$ npm install --save cowsay@1.0.0

yarn であれば、 yarn upgrade [パッケージ名]@[version] で指定したバージョンにそのままダウングレードしてくれます。もちろんアップグレードも可能。

$ yarn upgrade cowsay@1.0.0

yarn は楽でいいですね。個人的には npm と違ってデフォルトでローカルパッケージインストールなのが安心です。

【React Native/Expo】Google Oauth認証で認証後リダイレクトを行う

f:id:rennnosukesann:20190101154829p:plain:w500

前回の記事では、Expoを使用して作成したReact NativeアプリにGoogleOAuth認証を実装しました。

rennnosukesann.hatenablog.com

今回はその認証後にリダイレクト先URLを設定し、リダイレクト先にログインユーザの情報にアクセスするためのトークンを渡す手順について書いていきます。

WebクライアントIDの作成

認証後にリダイレクト可能なエンドポイントを設定するため、WebクライアントIDを作成します。

下記Google認証設定のページに遷移し、「認証情報の作成」ボタンをクリックします。

console.developers.google.com

f:id:rennnosukesann:20190102181936p:plain

ボタンをクリックすると作成したい認証情報のメニューが表示されるので「OAuth クライアントID」をクリック。

f:id:rennnosukesann:20190102182035p:plain

作成するクライアントIDの種類を選択します。今回はWebクライアントIDが欲しいので「Webアプリケーション」を選択。

f:id:rennnosukesann:20190102182418p:plain

すると詳細設定用のフォームが出現するのでIDの名称を入力し、リダイレクトしたいURLのドメインを「承認済みのJavaScript生成元」に、「承認済みのリダイレクトURL」にURLをそれぞれ入力します。入力が完了したら「作成」をクリック。

f:id:rennnosukesann:20190102182730p:plain

するとIDが作成されます。同時にクライアントIDを掲載したダイアログが表示されるので、IDをコピーしましょう。以降の工程ではこのIDをクライアントIDとして使用します。

f:id:rennnosukesann:20190102183120p:plain

アプリケーション側の実装

アプリケーション側の実装についても触れていきます。 以降の実装では、下記記事のアプリケーション作成を前提としています。

rennnosukesann.hatenablog.com

App.js

前回の記事 で作成した App.js 中の signInWithGoogle を以下のように変更します。

authUrl のパラメータである client_id には 先程取得したWebクライアントIDを入力してください。また redirect_url にはリダイレクト先のURLを入力してください。

  async signInWithGoogle() {
    try {
      const result = await Expo.AuthSession.startAsync({
        authUrl:
          `https://accounts.google.com/o/oauth2/v2/auth?` +
          `&client_id={WebクライアントID}` +
          `&redirect_uri={リダイレクトURL}` +
          `&response_type=code` +
          `&access_type=offline` +
          `&scope=profile`,
      });
     if (result.type === 'success') {
        console.log('Google Access Token: ' + result.accessToken);
      }
    } catch (e) {
      console.log(e);
    }
  }

Backend

今回は認証後のリダイレクト先として自前のバックエンドAPIサーバを指定すします。例として以下のようなSpring実装のAPIエンドポイントを用意し、Google OAuth認証後はこのエンドポイントに対してリクエストが飛びます。

下記実装ではAPIリクエストが届くとバックエンド側のコンソールにトークンIDが表示されるようにしています。実際はトークンIDを利用して、Google認可ユーザの情報を取り扱ったりすることが多いです。

@RestController
public class OAuthController {

@RequestMapping(value = "/oauth/google", method = RequestMethod.GET)
  public void signInWithGoogle(@RequestParam String code) {
      System.out.println(code);
  }

}

Demo

それでは、実際にアプリを動かしてみます。

npm start でExpo Developer toolsを起動し、QRコードをクライアント端末でスキャンしインストールを開始します。詳細な手順は下記記事に載っているので、適宜参照していただければ幸いです。

rennnosukesann.hatenablog.com

アプリを端末にインストールして起動したら、中央の「Sign in with Google」 ボタンを押します。

f:id:rennnosukesann:20190102130012p:plain:w300

すると 「Expoがサインインのために"expo.io"を使用しようとしています`」と書かれたダイアログが出現するので、「続ける」をタップします。

f:id:rennnosukesann:20190102201704p:plain:w300

続けて起動したアプリが別のサービス上でログインすることへの許可を求めてくるので、「Yes」をタップ。

f:id:rennnosukesann:20190102201749p:plain:w300

するとGoogleの認証画面に遷移します。すでに端末上でGoogleログイン済みのアカウントがある場合、アカウント一覧からログインしたいアカウントを選択します。

f:id:rennnosukesann:20190102130236p:plain:w300

認証に成功するとリダイレクトが実行され、バックエンドログにトークンIDが表示されているのがわかります。

2019-01-02T20:13:33.22+0900 [APP/PROC/WEB/0] OUT aBcd...(トークンIDが続く)

これでリダイレクト成功です!


OAuth認証は自前のサービスに組み込んで認証に使う場合が多く、リダイレクトでGoogleなどのSNS上にあるユーザ情報にサービスがアクセスさせなければいけない場面も割と多いので、今後も頻繁にメモしていきたいですね。

参考文献

Google - Expo Documentation