NetBeansでMavenなSpigotプラグインをKotlinする


#がいよー！
## NetBeans
かつてのEclipseのSwingライクなルックアンドフィールがトラウマとなりNetBeansを使用して早数年。
今でも世間の主流はEclipseで玄人はIntelliJ IDEAな感じのご時世ですが、無料だとか日本語だとかそもそもあんまりコーディングしないとか、理由をつけつつこれからもNetBeansをしばらく使っていく所存です。
そもそもNetBeansですら使いこなせていない＋お仕事でIDEA検証した際に操作スタイルに合わなかった等の諸々の理由もあります。

## Kotlin
こっとりーん、本記事の主目的です。
### ちょっと(???)だけKotlinについて
Kotlin自体は良い言語だけど新鋭過ぎて旧来のメジャーな高水準言語コーダー置いてけぼり感もあり、学習意欲のある人向け言語と言った所感。Scalaに近い形式の言語になっているのでメリットもデメリットもScalaチック、一説ではJVM言語に理解がある場合にはスムーズに理解できる話も見かけました。JREの互換性がそれなりに優秀なせいでJava8の新機能ですらあまり使用されていない現状があり、Kotlinに至っては現時点においては使える人が非常に限られるので個人開発向けかなぁという印象です。
もう少し込み入った話をすると「プログラミングができる」事は段階が存在するのは周知の事実と思いますが、その段階においてオブジェクト指向や関数型プログラミングの理解が十分できていないと、Kotlinのような効率化のために記載を省略していく言語は十分に理解できない事が想像できます。
形式的な手続き型言語の感覚では論外で、コピペプログラミングも同様と思いますが、実のところ「**オブジェクト指向で設計が出来る**」こともさほど**重要ではない**と思っています。どちらかと言うとプログラミングの理解度の段階を容易に上がれるようにする為に必要なのは、まず「**コードと概念を如何に早く的確に脳内変換できるか**」に掛かっていると言えないでしょうか。もっというと、如何に脳内にオブジェクトを想像し、それを操作参照出来るかに掛かっていると思います。ある程度自由に違和感なくコード中のオブジェクトと脳内のリアルイメージの物体を対応付けできるようになって、そこで初めてKotlinのようなオブジェクトの操作や情報にまつわる簡略化が理解できることでしょう。そういった意味だと、一部のオブジェクトがゲーム内で視覚化されるMinecraftのマルチサーバープラグインやクライアントMODと言った物はオブジェクト指向の考え方を学ぶには凄く良い教材ですね。
### NetBeansでKotlin
すごく話が逸れましたが、NetBeans向けのKotlin環境構築の情報が全然ありません。Googleを全言語検索にしても殆ど見当たりません。Wikipediaの記述( https://ja.wikipedia.org/wiki/Kotlin )にすら**Eclipseにもプラグインあるよ**ぐらいしか書かれていません…

## Maven
Kotlinが最近の言語だったりIDEA中心の言語だったりと言った理由があるんだとは思いますが、Gradleの解説ばかりが多くMavenで取りあえずビルドできる程度に構築したいと言った需要があまり見受けられませんでした。
Gradleも統合管理の観点では悪くは無いんですが、良くも悪くも覚えることが多い上、もはや定義だけでなく論理を構築(ないし理解)する必要性がある事から、お手軽さではMavenには劣ると思うのでもう少し情報が多くても良いのでは…。
とりあえず本稿では主にEclipse向けの手法や、Mavenの基本的な手法を調べてSpigotプラグイン向けにまとめました。

## Spigot
とりあえずSpigot向けに記載しますが、勿論BukkitプラグインでもSpigotプラグインでもBungeeCordプラグインやSpongeプラグインもKotlinで書けないはずが無いので、是非NetBeans+Kotlin+Mavenも視野に入れてやってください。

#やってみよー
多分がいよーより短い

##環境
- NetBeans IDE 8.1 (多分これ以外はあまり影響無いと思います)
- Java8

##NetBeansでプラグイン導入
####１. NetBeansの「ツール」⇒「プラグイン」から「Kotlin」にチェックを入れて「インストール」
![1.png](https://qiita-image-store.s3.amazonaws.com/0/166021/6a573885-44ae-6c0f-834f-e9134627d52c.png)
####２. 再起動してインストール完了

##Mavenプロジェクトを作る
####３. 新規プロジェクト作成から通常のMaven Javaアプリケーションプロジェクトテンプレートで作成。プロジェクトの設定は割愛。
![2.png](https://qiita-image-store.s3.amazonaws.com/0/166021/a079f53b-6805-2afe-131d-bd372ba355b2.png)

##pom.xmlの変更
変更箇所について抜粋していきます

####４. Kotlinの使用バージョン\<kotlin.version\>をプロパティに入れておく。
バージョンは後でdependency追加の際に何があるか確認すれば良い。spigotのバージョンもプロパティ化しても良いかもしれないが、複数個所で参照しないのであれば直接利用箇所に記載した方が楽。

```xml:pom.xml
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <kotlin.version>1.0.6</kotlin.version>
    </properties>
```
####５. Spigotリポジトリの追加

```xml:pom.xml
    <repositories>
        <repository>
            <id>spigot-repo</id>
            <url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
        </repository>
    </repositories>
```

####６. (オプション)KotlinのSnapshotリポジトリの追加
リリース版はMavenのセントラルリポジトリに登録されているので、snapshot指定しないなら不要なはず？
Snapshotを試す場合は以下を\<repositories>に追加する。

```xml:pom.xml
        <repository>
            <id>sonatype.oss.snapshots</id>
            <name>Sonatype OSS Snapshot Repository</name>
            <url>http://oss.sonatype.org/content/repositories/snapshots</url>
            <releases>
                <enabled>false</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
```

####７. (オプション)KotlinのPlugin Snapshotリポジトリの追加
Snapshotを試す場合は以下を追加する。


```xml:pom.xml
    <pluginRepositories>
        <pluginRepository>
            <id>sonatype.oss.snapshots</id>
            <name>Sonatype OSS Snapshot Repository</name>
            <url>http://oss.sonatype.org/content/repositories/snapshots</url>
            <releases>
                <enabled>false</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>
```

####８. SpigotとKotlinの依存関係の追加
それぞれバージョンは自由に。リポジトリが正しく追加できていれば、Spigot/Kotlinいずれも\<version>の直後でCtrl+Spaceを押すことで選択可能なバージョンの候補が出てくる。Kotlinのバージョンはプロパティ定義の方に書いてやること。

```xml:pom.xml
    <dependencies>
        <dependency>
            <groupId>org.spigotmc</groupId>
            <artifactId>spigot-api</artifactId>
            <version>1.10.2-R0.1-SNAPSHOT</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-stdlib</artifactId>
            <version>${kotlin.version}</version>
        </dependency>
    </dependencies>
```

####９. ビルド定義の追加
- \<resources>はplugin.ymlを同梱する設定
- \<plugin>のcompile定義はKotlinに.ktファイルをコンパイルさせる設定
- \<plugin>のshade定義は依存ライブラリをまとめてjarに同梱する設定 **※オプション**

依存ライブラリを同梱しない場合、Kotlin-runtimeの依存関係が解決できずにプラグインのロードが失敗する場合がある。
Kotlin-runtimeをJarに同梱する場合はプラグインサイズが600KB程度増加する。Kotlin製プラグインを複数導入する場合は、一つをKotlin-runtime同梱にして、それ以外のプラグインを同梱無しにしてKotlin-runtime同梱プラグインの後発ロード指定をすると良い。

```xml:pom.xml
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>plugin.yml</include>
                </includes>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <artifactId>kotlin-maven-plugin</artifactId>
                <groupId>org.jetbrains.kotlin</groupId>
                <version>${kotlin.version}</version>
                <configuration/>
                <executions>
                    <execution>
                        <id>compile</id>
                        <phase>process-sources</phase>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>test-compile</id>
                        <phase>test-compile</phase>
                        <goals>
                            <goal>test-compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>2.4.3</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <filters>
                                <filter>
                                    <artifact>org.spigotmc:*</artifact>
                                    <excludes>
                                        <exclude>***</exclude>
                                    </excludes>
                                </filter>
                            </filters>
                            <minimizeJar>true</minimizeJar>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
```

##plugin.ymlの変更
以下はプラグインにKotlin-runtime同梱した場合の例。
Kotlin-runtimeが含まれないプラグインは、含まれるプラグインを `depend: [TestKotlin]` のような形で依存関係を定義する。

```yaml:plugin.yml
name: TestKotlin
main: jp.minecraftuser.testkotlin.TestKotlin
version: 0.1
commands:
    kotlin:
        description: Kotlin command.
        usage: kotlinlin
    kotadm:
        description: Kotlin admin command.
        usage: admin kotlinlin
```

##サンプルのメインクラス
以下はメインクラスのサンプル

```kotlin:TestKotlin.kt
package jp.minecraftuser.testkotlin

import org.bukkit.plugin.java.JavaPlugin

class TestKotlin : JavaPlugin() {
    // アクセスしやすいように変数にいれておく
    val log = getLogger() 

    // onEnableのオーバーライド実装
    override fun onEnable() {
        val cmd = MyCommand(this)
        getCommand("kotlin").setExecutor(cmd);
        getCommand("kotadm").setExecutor(cmd);
        MyListener(this)
        log.info("${getName()} plugin enable")
    }

    // onDisableのオーバーライド実装
    override fun onDisable() {
        log.info("${getName()} plugin disable")
    }
}
```

##サンプルのコマンドクラス
以下はコマンドクラスのサンプル

```kotlin:MyCommand.kt
package jp.minecraftuser.testkotlin

import java.util.logging.Logger
import org.bukkit.command.Command
import org.bukkit.command.CommandExecutor
import org.bukkit.command.CommandSender

open class MyCommand(val plg : TestKotlin, val log : Logger = plg.getLogger()) : CommandExecutor {
    // デフォルトコンストラクタ
    // ログ出してるけどinitごと無くてもOK
    init {
        log.info("generate MyCommand")
    }
    
    // コマンド実行処理
    override fun onCommand(sender: CommandSender, cmd: Command, str: String, params: Array<out String>): Boolean {
        // 復帰値に直接when(スカラのmatchみたいなの)の結果を返却させる
        return when (cmd.getName().toLowerCase()) {
            // kotlinコマンドの処理
            "kotlin" -> {
                sender.sendMessage("わーい！なにこれなにこれ！すごーい！たーのしー！")
                // コマンド実行結果はtrueで返す
                true
            }
            // kotadmコマンドの処理
            "kotadm" -> {
                // パラメタがあったらサブコマンドとみなす
                if (!params.isEmpty())
                    // サブコマンドの解析
                    when (params[0].toLowerCase()) {
                        // kotadm reloadコマンドの処理
                        "reload" -> {
                            sender.sendMessage("りろーど処理")
                            plg.reloadConfig()
                            true   
                        }
                        // kotadmのパラメータが意図しないものであれば復帰値にfalseを返す
                        else -> false
                    } 
                else
                    false
            }
            // それ以外はfalseで返しておく
            else -> false
        }
    }
}
```

##サンプルのイベントリスナクラス
以下はイベントリスナクラスのサンプル

```kotlin:MyListener.kt

package jp.minecraftuser.testkotlin

import java.security.SecureRandom
import java.util.logging.Logger
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.player.PlayerJoinEvent

class MyListener(val plg : TestKotlin, val log : Logger = plg.getLogger()) : Listener{
    // デフォルトコンストラクタ
    init {
        // インスタンス生成時にイベントリスナの登録をする
        plg.getServer().getPluginManager().registerEvents(this, plg);
        log.info("register MyListener")
    }

    // 入店処理    
    @EventHandler
    fun joinCafe(e: PlayerJoinEvent) {
        log.info("detect kotlin PlayerJoin")
        // プレイヤーに送信
        e.getPlayer().sendMessage("""
            |ふわああぁ！いらっしゃぁい！よぉこそぉ↑マイクラ鯖へ～！
            |どうぞどうぞ！ゆっぐりしてってぇ！いやま゛っ↓てたよぉ！
            |やっとプレイヤーさんが来てくれたゆぉ！嬉しいなあ！
            |ねえなんにぃするぅ 色々あるよぉ、これね、${arrayOf("アスレチック","迷路","エンドラ戦","RTA").let { it.get(SecureRandom().nextInt(it.size)) }}って言うんだってぇ
            |${arrayOf("管理人","実況者","動画投稿者","ぺこ").let { it.get(SecureRandom().nextInt(it.size)) }}さんに教えてもらったンの！ここからスタートだから頑張ってにぇ
            |""".trimMargin().split("\n".toRegex(), 0).toTypedArray())
    }
}
```

#まとめ

なにこれ！なにこれ！すっごーい！わーい！

NetBeansでもKotlinできるじゃん。