Codenvy (Eclipse Che) で Spring Boot + Gradle ウェブプロジェクトを動作させる

ブラウザ版 Eclipse である Eclipse Che は、Docker を活用しさまざまな言語の開発環境をクリックひとつで構築でき、ブラウザから操作できる利点と合わせ「どこでも開発」が実現できるんじゃないかと思わせる、現在も活発に開発が進んでいる期待のプロジェクトです。 😀

今回、Eclipse Che の Saas である Codenvy を使って Spring Boot + Gradle ウェブプロジェクトを動作させるケースで、ハマりポイントや手間が結構ありましたので設定を記載したいと思います。

現在 Eclipse Che 上で Spring Boot + Gradle を動かそうとすると、いくつかの問題点があります。

  • Gradle (build.gradle) を解釈してくれないないため、ソースやクラスパスを通すことができない。 Eclipse でいうところの buildship 相当の実装がまだされていない。
  • IDE にインクリメンタルビルドが実装されてない(ですよね?)ため、Spring Boot DevTool のリロード機能を使うことができない。
  • Codenvy のリソースの問題か、ブラウザ上で動作する JS の問題か、結構フリーズする…

最後の問題はさておき、上の 2点は gradle を駆使してなんとかすることができましたので、Codenvy のログイン後からの手順を書いてみます。

Workspace の作成

プロジェクトを動作させるためのワークスペースを作成します。左メニュー Workspaces から Add を押下します。

SELECT STACK のフィルターで All を選択し、検索文字列に java debian を入力し “Java Debian” スタックを選択します。(2018/06/09 時点で CentOS を使っている Docker イメージを選択すると Workspace 開始がエラーになりましたので Debian にしています)

また、マシンのメモリーはデフォルト 2G になっていますが、3G まで無料で使えるようなので上げてしまってもいいかもしれません。

次に Spring Boot + Gradle (gradlew 入り)のソースコードを git から取得するため Add Project に git の Repository を指定します。

ここではサンプルで自分が公開している Spring Boot + Vue.js + Gradle + webpack のテンプレートプロジェクトを指定しています。

https://github.com/h1romas4/springboot-template-web.git

保存したら Create ボタンを押下してワークスペースを作成します。作成後、画面がワークスペースに遷移し Docker で環境が構築され起動し、git からリポジトリーからソースコードが取り込まれます。

build.gradle を修正し pom.xml を生成する

Eclipse Che は現在 build.gradle を認識できないため、このままであると .java はただのテキストファイル扱いで補完などができません。これを解決するため、Eclipse Che が扱える Maven の pom.xml を生成します。

Gradle に pom.xml を生成するプラグインがありますので、プロジェクトの build.gradle を次のように修正します。

maven プラグインを追加。(と maven プラグインが要求するため groud, version を追加)

plugins {
    id 'java'
    id 'eclipse'
    id 'maven'
    id 'com.moowork.node' version '1.2.0'
    id 'org.springframework.boot' version '1.5.9.RELEASE'
}

group = 'com.example'
version = '1.0-SNAPSHOT'

次に、dependencies に使うバージョンを追記します。(Gradle の Spring Boot プラグインは指定したプラグインのバージョンから、依存関係のライブラリバージョンも自動で認識してくれますが、Maven プラグインからは参照できないため)

また合わせて、gradle build 時に pom.xml が自動的に生成されるようにタスクを追加します。(task createPom 以降)

dependencies {
    compile('org.springframework.boot:spring-boot-starter-web:1.5.9.RELEASE')
    compile('org.thymeleaf:thymeleaf-spring4:3.0.2.RELEASE')
    compile("org.springframework.boot:spring-boot-devtools:1.5.9.RELEASE")
}

task createPom {
    description "Generates a pom.xml in the project root directory; useful e.g. for IDEs which can read POM but don't directly support Gradle."
    doLast {
        pom {
        }.writeTo("pom.xml")
    }
}

compileJava.dependsOn createPom

build.gradle の修正が終わったら、画面下部の Terminal から次のコマンドを入力し初期ビルドします。

$ cd springboot-template-web/
$ ./gradlew build
$ mvn package
$ ./gradlew build

mvn package の後に再度 ./gradlew build しているのは”おまじない”です。(pom.xml に刺激を与えることで Eclipse Che が Maven プロジェクトとして認識してくれるようです。この手順を確立するまでハマりました。。)

Eclipse Che が pom.xml を認識するとプロジェクトのツリーに External Libraries として無事 dependencies が入るはずです。

.java も Java ソースファイルとして扱ってくれ、補完なども効くようになります。

アプリケーションの起動

Spring Boot のウェブアプリケーションを起動するために実行構成を作成します。

Create new command から bootRun タスクを次のように作成します。

Command Line

cd ${current.project.path} && ./gradlew bootRun

Preview URL

${server.8080}

なお、それぞれの入力部分はマルチラインになっていますが、必ず 1行で書いてください。空行でも改行コードが混在すると Windows(のブラウザ)では動かなくなります。どうやら LF スプリットしているらしく Linux なら動作してしまうという…(ハマり2)

Save 後 RUN ボタンでアプリケーションが起動します。

タスクのビューの preview: 欄にある URL をクリックするとポート 8080 で起動したアプリケーションにブラウザから接続でき、実行を確認することができます。

なお、アプリの停止は画面上部右の「■」から行えます。

インクリメンタルビルドとリロード

この Spring Boot プロジェクトには Spring DevTool の依存関係が入っており、Eclipse ではソースを修正すると自動的にアプリケーションが再起動し、ビルドアンド確認が簡単にできるように設定されています。

残念ながら Eclipse Che はソース修正後に自動的にコンパイルは走らないようなので、bootRun しているとは別の gradle を使って、ウォッチとインクリメンタルビルドをしてあげます。

Terminal から次のコマンドを入力します。

./gradlew build --continuous

これでファイルウォッチに入りますので、.java ファイル修正でアプリケーションが自動的に再起動されるはずです。(停止は Ctrl + d です)

webpack ビルド

このプロジェクトは webpack/nodejs のビルドを使い、JavaScript を ES2015 としたり CommonJS を取り入れたりしています。 nodejs のランタイムは Gradle のプラグインにより自動的にダウンロードされるようになっていますので、上部メニューの Run から Terminal で新しいターミナルを開き次のコマンドを入力するとビルドがかかります。

./gradlew webpack

また、JavaScript の修正からの自動ビルドをするには次のコマンドを入力するとウォッチモードに入ります。

./gradlew watch

詳しくは「フロントエンド技術を導入した Java ウェブアプリケーション開発」をご覧ください。

さて、Eclipse Che にて .js を開くと…

おっ(ΦдΦ)!!という感じになりましたが、自分の環境ではこの後ブラウザが何度やってもフリーズしてしまいました。。いい感じに動きそうなだけに、無念。。

最後に

実際に Codenvy で Eclipse Che を使ってみるとフリーズも多くまだまだこれからといった印象ですが、今後はエディタ部が Eclipse Orion ベースのものから VS Code でおなじみ Monaco Editor になるなど改良が進んでいくようです。

個人的にはブラウザから使うより、Eclipse Che の REST API を活用して任意のクライアントから Docker 環境や language-server に接続できる方向にも発展してくれると嬉しいですが、継続してウォッチしていきたいと思います。

とりあえず、本手順でいろいろ検証できると思いますので、余暇にでもお試しください。 🙂

フロントエンド技術を導入した Java ウェブアプリケーション開発

Visual Studio Code Advent Calendar 2017 の 11日目の記事です。 🙂

今年の自分は Java と PHP と JavaScript と電子工作の年だったような気がするのですが、そんな中、全てにおいて随分お世話になりました VS Code。Microsoft から Arduino 拡張がリリースされたのが一番驚きました。

ということで、今日より 3日間 VS Code について書いていこうと思います。まず最初は Language Support for Java(TM) by Red Hat(vscode-java) から。「フロントエンド技術を導入した Java ウェブアプリケーション開発」です。

この記事では VS Code で Java とフロントエンド系の開発を行うサンプルプロジェクトを準備しました。 簡単な操作で昨今のフロントエンド技術を使った Java ウェブアプリケーションのビルドができるようになっていますので、興味がある方は手順を追って試していただければと思います。

OS は macOS、Linux、Windows と VS Code がオフシャルサポートする全ての環境で動作することを確認しています。

準備

お使いの環境に次のソフトウェアを導入してください。

  • Java (JDK) 8 以上
  • VS Code 1.18 以上

JDK については、コマンドライン・ターミナルから java -version コマンドが次のように実行できていれば準備完了です。(Linux の例)

$ java -version
openjdk version "1.8.0_151"
OpenJDK Runtime Environment (build 1.8.0_151-8u151-b12-0ubuntu0.17.10.2-b12)
OpenJDK 64-Bit Server VM (build 25.151-b12, mixed mode)

また、VS Code には次の拡張を導入してください利用します。Language Support for Java(TM) by Red Hat が必須の拡張になります。

更新:プロジェクト読み込み後に次のダイアログが出力されますので「すべてインストール」を選択すると自動的に導入されます。 また、左サイドバーのプラグインの「推奨」設定に必要なプラグインの一覧も表示されます。

ESLint 及び HTMLHint プラグインについては動作に Node.js の実行環境が必要となりますが、本サンプルプロジェクトのビルドで自動導入されますので別途操作は不要です。

最後に Windows をお使いの方は、VS Code のファイル -> 基本設定 -> 設定より、vscode-java の java.jdt.ls.vmargs プロパティーに対して次の設定をしてください。

{
  "java.jdt.ls.vmargs": "-noverify -Xmx1G -XX:+UseG1GC -XX:+UseStringDeduplication -Dfile.encoding=UTF-8"
}

vscode-java の起動引数の最後に “-Dfile.encoding=UTF-8” を追加します。(更新:プロジェクトのワークスペース設定に事前に設定しましたので操作は不要になりました)

Windows 版の JDK ではデフォルトファイルエンコードが Shift_JIS になっていますが、本サンプルプロジェクトの文字コードは UTF-8 です。この設定により、ソースコード中に直接日本語を書いた場合に vscode-java の自動ビルドで実行するアプリケーションに文字化けが発生するのを防ぎます。

サンプルプロジェクトのダウンロード及び初期ビルド

github から次の操作でサンプルプロジェクトをクローンし、初期ビルドをかけてください。この操作は一度だけで大丈夫です。

git の導入がされていない方は git clone の代わりに .zip ファイルをダウンロード・展開した後、ビルドすれば OK です。

プロジェクトフォルダを配置する場所はどこでも大丈夫ですが、日本語パスを含む場所は避けてください。(後述する Spring Boot 開発者ツールのファイル監視&自動リロード処理が動かないケースがあります)

また、会社や校内などで http/https プロキシー環境下にある場合は、ビルド実行前に次のようなコマンドでプロキシーを設定してください。(環境が固定されている場合は gradlew.bat / gradlew の先頭に書き加えてもOKです)

Windows の場合(cmd.exe もしくは powershell.exe で実行)

set JAVA_OPTS=-DproxyHost=myproxy.co.jp -DproxyPort=8080
set HTTP_PROXY=http://myproxy.co.jp:8080
set HTTPS_PROXY=http://myproxy.co.jp:8080

macOS、Linux の場合(ターミナルで実行)

export JAVA_OPTS=-DproxyHost=myproxy.co.jp -DproxyPort=8080
export HTTP_PROXY=http://myproxy.co.jp:8080
export HTTP_PROXYS=http://myproxy.co.jp:8080

準備ができたらビルドします。(プロキシーを設定した場合は同じ窓で引き続き入力してください) Windows の場合(cmd.exe もしくは powershell.exe で実行)

git clone https://github.com/h1romas4/springboot-template-web.git
cd springboot-template-web
.\gradlew.bat build
.\gradlew.bat eclipse
.\gradlew.bat webpack
code .

macOS、Linux の場合(ターミナルで実行)

git clone https://github.com/h1romas4/springboot-template-web.git
cd springboot-template-web
./gradlew build
./gradlew eclipse
./gradlew webpack
code .

初期ビルドはライブラリのダウンロードを伴うため少し時間がかかります。

サンプルプロジェクトは Gradle を使ってビルドされます。 gradle wapper を使っているため gradle の導入は不要です。 手順では 3つの gradle タスクを実行しており、それぞれ次のような動作を行っています。

./gradlew buiild

サンプルプロジェクトにある build.gradle を元に Java アプリケーションをビルドします。サンプルは Spring Boot を用いたウェブアプリケーションとなっていますが、必要となるライブラリ jar をインターネットから自動的にダウンロードし、アプリケーションが実行可能なように構成します。

./gradlew eclipse

VS Code の記事なのに eclipse と指定されていて不思議な感じですが、実は vscode-java(Language Support for Java(TM) by Red Hat) の実体は Eclipse の JDT と buildship という機能を language-server として外に出し、VS Code から使えるようにしたものです。

このことから vscode-java は Eclipse 用の .project や .classpath ファイル、そして build.gradle を解釈できるようになっています。 この gradle の eclipse タスクは、build.gradle から .project や .classpath を実行環境に合わせて構成するものです。起動した vscode-java はこれらのファイルを参照し、Java の編集・ビルド環境を初期化します。

./gradlew webpack

本サンプルプロジェクトでは以下の Node.js の実行環境を使ったフロントエンドのビルド技術を活用しています。

gradle の webpack タスクではこれらのセットアップを gradle-node-plugin を使って行っています。 gradle-node-plugin は Node.js の実行環境を自動的にインターネットからダウンロードし、package.json を参照して必要なモジュールを node_modules にプロジェクトローカルとして格納します。(内部的には、使っている OS の Node.js 実行環境を自動的に選択、ダウンロードし npm install コマンドを発行しています)

アプリケーションの実行

これにて VS Code によるウェブアプリケーション開発の準備完了です。まずはアプリケーションを実行してみましょう。

VS Code の統合ターミナルを起動し、次の gradle タスクを実行してください。 Windows の場合

.\gradlew.bat bootRun

macOS、Linux の場合

./gradlew bootRun

ターミナルに “Started Application” と表示されたら準備完了です。

ブラウザから http://127.0.0.1:8080/ にアクセスしてみます。

おめでとうございます。 🙂

では、VS Code に戻って src/main/java/HomeController.java を開き「こんにちは。」となっているところを「こんにちは!」などと書き換えてみてください。先ほど統合ターミナルで起動した gradle bootRun タスクが自動的に .java ファイルの編集・ビルドを検知し、アプリケーションにリロードがかかり、ブラウザを更新すると修正した文字列で動作することと思います。

この動作は build.gradle ファイルの次の指定によるものです。

eclipse {
  classpath {
    defaultOutputDir = file('build/classes/java/main')
  }
}
dependencies {
  // ...
  compile("org.springframework.boot:spring-boot-devtools")
}

gradle eclipse タスクで、vscode-java が .java ファイルを保存後に自動ビルド .class ファイルの格納先を gradle bootRun タスクが実行に使う build/classes/java/main に指定しています。

また、spring-boot-devtools を依存に入れることで、bootRun タスク中に build/classes/java/main 内の .class ファイルの書き換えを監視し自動的にアプリケーションのリロードがかかるようになります。(なお、2017/12 時点最新の gradle 4.4 では eclipse タスクに修正がかかっておりこの記述だけでは動作しないため、今回は暫定的に 4.3 を指定して使っています。更新:build.gradle を修正し Gradle 4.9 にアップデートしました)

さらに、このアプリケーションで使われている JavaScript も修正してみましょう。本サンプルプロジェクトは、JavaScript の記述に webpack/babel を用いた CommonJS/Modue、ES2015 が使えるようにセットアップされています。

gradle bootRun タスクが動作中の VS Code の統合ターミナルの右側に配置されている「+」アイコンをクリックし、さらにもう一つ統合ターミナルを起動し、以下のコマンドを実行してください。 Windows の場合

.\gradlew.bat watch

macOS、Linux の場合

./gradlew watch

Module/ES2015 で記述された src/main/js/app.js が監視され、修正を検知しブラウザで動作する形にコンパイルされ、実際に .html から読み込まれる resources/static/js/bundle.js に出力されるようになります。

ためしに app.js を書き換えてみると監視が動作し、bundle.js が書き換わるのが分かります。 開発時の手順は以上です。フローをまとめると次のようになります。

  1. VS Code でプロジェクトのフォルダを開く。
  2. 統合ターミナルで gradlew boouRun を実行する。
  3. JavaScript の開発をする際はもう一つ統合ターミナルを起動して gradlew watch する。
  4. ソースファイルを編集して開発を進める。

サンプルプロジェクトは Spring Boot のウェブアプリケーションとなっています。ソースコードを修正しながら VS Code の機能とプログラミングを楽しんでみてください。

Language Support for Java(TM) by Red Hat

vscode-java では現在、Java 編集時のリアルタイムコンパイルエラー検出、定義ジャンプ、インテリセンス(補完)などの基本的な機能がサポートされています。

まだクイックフィックスやソースのオートジェネレート系は弱いものの、auto import が実装されましたのでほとんどの場面で困ることはなくなりました。逆にこのほうが Java の勉強になっていいかもです… 😛

import の構成は解決できないパッケージ(赤波線)の上で一度クリックすることで表示される、電球アイコンから行うことができます。

JavaScript

VS Code は標準で JavaScript の編集が非常に強いエディタです。

package.json ファイルを自動で認識して、モジュール構成の JavaScript ファイルであれば適切に IntelliSense することができます。補完は TypeScript 用の型定義を見てくれるようで非常に快適です。

VS Code は Module/ES2015 でソースが書かれていることを認識しますので、例えば Vue.js ライブラリを import した状態で IntelliSense を発動させると次のように Vue がもつプロパティーを表示してくれたりします。(便利!)

また、利用するパッケージを定義する package.json の記述では、次のようにパッケージの名称やバージョンを補完することができます。(便利!) プロジェクトで利用する JavaScript のライブラリを増やす手順は次のようになります。

  1. package.json の dependencies に(IntelliSenseをつかいつつ)必要なライブラリとバージョンを指定する。
  2. gradle watch  タスクを実行し、ライブラリを自動的にインターネットからダウンロードさせる。(watch タスクは必要な依存ライブラリ全てを動作するように bundle.js に固めてくれるため .html の .js 読み込み部分に修正の必要はない)
  3. JavaScript のソースコードで import を構成して、(IntelliSenseで補完しつつ)コーディングする。

非常に快適です。 🙂

ESLint

サンプルプロジェクトでは package.json の devDependencies に eslint を設定しています。node_modules に eslint があると、VS Code の ESLint 拡張が働くようになり、上のスクリーンショットのように JavaScript の誤りをリアルタイムに検出してくれます。 ソースコードが揃いますので入れておくと便利だと思います。 サンプルプロジェクトでは eslint の設定ファイルである .eslintrc.js を最小限にしていますので、必要に応じてルールを修正してみてください。

HTMLHint

ESLint と同様に node の htmlhint を活用した拡張です。

HTML のタグの閉じ忘れや誤ったマークアップなどをリアルタイムで指摘してくれます。 サンプルプロジェクトでは、テンプレートエンジンに Thymeleaf を使っていますが、このような HTML を壊さない系のテンプレートと非常に相性が良いと思います。

HTML & CSS

VS Code 標準で HTML の emmet 入力及び CSS の IntelliSense に対応しています。 VS Code の emmet は今年に入り 2.0 化され、プレビューができるなど便利になりました。

CSS の自動補完も i マークを押すことで対応ブラウザが出力されるなど便利になっています。

EditorConfig for Visual Studio Code

プロジェクトのルートファイルにある .editoerconfig ファイルをみて、ソースコードの文字コードや改行、タブ指定などを揃えてくれます。

root = true

[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

最近はさまざまな OS で開発することが多く、ソースコードの体裁を揃えるために必要な拡張となっています。(今回も 3 OS で検証中、Windows の git の改行コード変換が auto になっていたため、この拡張に助けられました…)

VS Code で Java 開発を始めよう

VS Code による Java 開発は適当にフォルダをつくり、ビルドを入れれば始められるという手軽さが魅力です。

特に、Java はできるが JavaScript は苦手という方にも、JavaScript に対して一般的なプログラミング言語と同等なモジュール構成や補完機能が Java 開発環境内で同時にサポートされますので、楽しくモダンなフロントエンドを学べる良い環境になるのではないかと思います。

お正月休みの研究にいかがでしょうか。

Groovy + Spring Boot + SWT でクライアントアプリケーションをつくる

G* Advent Calendar 2015 9日目です!

昨日は saba1024 さん「[Groovy] MongoDBを簡単に扱えるイケてる言語Groovy -Groovyの応用編-」でした!


 

職場などで業務改善的なツールをつくりたくなる場合がありますが、案外みんなの PC にスクリプト言語を動かす環境がなかったりします。そんな時は Groovy ! Java の現場であればそのままつくった jar を渡せますし、そうでなくても launch4j などで JRE ごと渡すことができます。

今回は「Groovy + Spring Boot + SWT」という組み合わせで、手軽に高速に GUI Groovy アプリケーションをつくる骨格を紹介してみたいと思います。

珍しい組み合わせかと思いますが、Spring Boot のオートコンフィグレーションと、後述する spring-loaded によるホットデプロイ(GUI 再起動無しで処理を変更できる)と Groovy によるプログラミングの組み合わせは、かなり高速に開発を進めることができると思います。

プロジェクトの構成は以下のような感じになります。

groovy-swt-02

まずは、build.gradle で Spring Boot と SWT を定義してあげます。

build.gradle

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.3.0.RELEASE'
    }
}

apply plugin: 'groovy'
apply plugin: 'spring-boot'

repositories {
    mavenCentral()
    maven { url 'http://maven-eclipse.github.io/maven' }
}

dependencies {
    compile 'org.codehaus.groovy:groovy-all:2.4.5'
    compile 'org.springframework.boot:spring-boot-starter'
    compile "org.eclipse.swt:org.eclipse.swt.win32.win32.x86:4.5.1"
}

springBoot {
    mainClass = 'sample.Application'
}

SWT は Windows x86 のものを選択していますが、他の環境の定義は次の通りです。

    // The Standard Widget Toolkit(SWT)
    // System.getProperty('os.name').toLowerCase().split()[0]
    // System.getProperty("os.arch")
    // 
    // Windows x86
    // compile "org.eclipse.swt:org.eclipse.swt.win32.win32.x86:4.5.1"
    // Windows x64
    // compile 'org.eclipse.swt:org.eclipse.swt.win32.win32.x86_64:4.5.1'
    // Linux GTK+ x86
    // compile 'org.eclipse.swt:org.eclipse.swt.gtk.linux.x86:4.5.1'
    // Linux GTK+ x64
    // compile 'org.eclipse.swt:org.eclipse.swt.gtk.linux.x86_64:4.5.1'
    // OS X Cocoa x64
    // compile 'org.eclipse.swt:org.eclipse.swt.cocoa.macosx.x86_64:4.5.1'

次に Spring Boot の規約に従って(必要なら)ロガー(logback-spring.xml)と Application.yml を定義します。

src/main/resources/logback-spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/base.xml"/>
    <logger name="sample" level="DEBUG"/>
</configuration>

src/main/resources/Application.yml

setting:
    defaultPath: C:\Users

Application.yml は Spring Boot の設定も記述できますが、作成するアプリケーションで外だししたい設定なども書けます。これを読むための、ApplicationSetting は次のようになります。

src/main/groovy/ApplicationSetting.groovy

package sample;

import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.stereotype.Component

@Component
@ConfigurationProperties(prefix = "setting")
public class ApplicationSetting {
    String defaultPath
}

Java でかくと Setter/Getter が必要ですが、Groovy ならこれだけです。.yml のキーと変数名を合わせれば勝手に Spring Boot がバインドしてくれます。

プログラムの起動点となる Application は次のようになります。

src/main/groovy/Application.groovy

package sample

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.SpringApplication
import sample.gui.Controller

@SpringBootApplication
class Application {

    @Autowired
    Controller controller

    static void main(args) {
        SpringApplication.run(Application.class, args).withCloseable {
            it.getBean(Application.class).controller.start()
        }
    }
}

通常の Java アプリケーションと同様に main を起動してあげると、Spring Boot が main があるパッケージ配下のコンポーネントを自動でスキャンしてクラスロードしてくれます。 @Autowired で GUI の Controller をインジェクションして main から呼び出しました。

呼び出される gui.Controller は次のようなものです。

src/main/groovy/gui/Controller.groovy

package sample.gui

import org.slf4j.*
import org.eclipse.swt.*
import org.eclipse.swt.events.SelectionListener
import org.eclipse.swt.graphics.*
import org.eclipse.swt.layout.*
import org.eclipse.swt.widgets.*
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import sample.ApplicationSetting
import sample.service.Convert

@Component
class Controller {

    static final logger = LoggerFactory.getLogger(Controller.class);

    @Autowired
    Convert convert

    @Autowired
    ApplicationSetting setting

    Display display
    Shell shell
    ToolItem open
    Table list

    def private init() {
        // Window Setting
        display = new Display()
        shell = new Shell(
            display, SWT.TITLE | SWT.RESIZE | SWT.MIN | SWT.MAX | SWT.CLOSE)
        shell.setSize(640, 480)
        shell.setText("Sample Application")
        shell.setLayout(new GridLayout())
        // Application Icon
        shell.setImage(new Image(display, this.getClass().getResourceAsStream(
            "/images/icon/calculator.32x32.png")))

        // Toolbar
        def toolbar = new ToolBar(shell, SWT.FLAT | SWT.RIGHT)
        // Open
        open = new ToolItem(toolbar, SWT.PUSH)
        open.setText('Open')
        open.setImage(new Image(display, this.getClass().getResourceAsStream(
            "/images/icon/start.32x32.png")))
        // Toolbar Size
        toolbar.pack()

        // List Table
        list = new Table(shell, SWT.FULL_SELECTION | SWT.BORDER)
        list.setHeaderVisible(true)
        list.setLayoutData(new GridData(GridData.FILL_BOTH))
        list.setFocus()

        // Event Listener
        open.addSelectionListener([
            widgetSelected : {  e ->
                def f = new FileDialog(shell, SWT.OPEN)
                f.setFilterPath(setting.defaultPath);
                f.setFilterExtensions(["*.csv"] as String[])
                def login = f.open()
                def message = convert.input(login);
                def box = new MessageBox(shell, SWT.OK | SWT.OK)
                box.setMessage(message)
                box.open()
            }
        ] as SelectionListener)
 
        shell.open()
    }

    public void loop() {
        while(!shell.isDisposed()) {
            if(!display.readAndDispatch()) {
                display.sleep();
            }
        }
        display.dispose()
    }

    def start() {
        try {
            init()
            loop()
        } catch(Exception e) {
            e.printStackTrace()
            def box = new MessageBox(shell, SWT.OK | SWT.ABORT)
            box.setMessage("例外が発生しました。\n" + e.message)
            box.open()
        }
    }
}

SWT の GUI 作成とメインイベントループがあります。

ソース先頭で、サービスクラスにあたる service.Convert と先ほど Application.yml を読むためにつくった ApplicationSetting を DI しています。

    @Autowired
    Convert convert

    @Autowired
    ApplicationSetting setting

ボタンを押したときのイベントでこれらを利用しています。

        // Event Listener
        open.addSelectionListener([
            widgetSelected : {  e ->
                def f = new FileDialog(shell, SWT.OPEN)
                f.setFilterPath(setting.defaultPath);
                f.setFilterExtensions(["*.csv"] as String[])
                def login = f.open()
                def message = convert.input(login);
                def box = new MessageBox(shell, SWT.OK | SWT.OK)
                box.setMessage(message)
                box.open()
            }
        ] as SelectionListener)

サービスクラスはとりあえず。

src/main/groovy/service/Convert.groovy

package sample.service;

import java.io.File;

import org.springframework.stereotype.Component;

@Component
public class Convert {
    def input(file) {
        return "converted!" 
    }
}

というわけで、Application.groovy を IDE から実行するか、./gradlew bootRun するとアプリケーションが起動します。

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

2015-12-09 00:25:31.353  INFO 6560 --- [           main] sample.Application 

groovy-swt-01

さて、ここで大技です。 spring-loaded を実行時に javaagent で読み込むことで、実行中に修正したクラスをリロードできるようになります。 プロジェクトサイトから jar をダウンロードし、アプリケーション実行時の JVM 引数に以下を加えます。

-javaagent:${project_loc:template-springboot-swt}/lib/springloaded-1.2.5.RELEASE.jar -noverify

“${project_loc:template-springboot-swt}” は Eclipse 的なプロジェクトホームの指定ですので、適宜変更してください。

groovy-swt-04

なお、Eclipse で実行する場合は Application.groovy から Run してください。./gradle bootRun だとリロードされません。これは、Eclipse の場合、自動コンパイルの .class ファイルを置く先が bin/ ディレクトリ配下となり、bootRun した場合は build/ 配下の class ファイルで実行され、ファイルの更新がされないためです。

というわけで、spring-loaded ですが、やってみると異常に快適です。 GUI をつくる場合、対象の処理にたどり着くまでの操作が長くかかることがありますが、spring-loaded を入れておくとリトライが簡単で、なんだか世界が変わります。 🙂

その他、Spring Boot 上であると、H2 の組み込み DB や、JPA や GORM などの O/R マッパーも、gradle の定義だけでオートコンフィグレーションされてすぐ使えるようになりますので、非常に便利です。

今回のサンプルでは、サービスクラスで CSV を H2 に読んで GORM で抽出をかけたかったのですが、出張先にて機材トラブルにより間に合いませんでした。ごめんなさい。。

以上、あまり手間をかけず、ゆるいフレームワーク規約で自由気ままにツールなどの GUI をつくりたいなんて時に、良ければお試しください。

最後に、Apache Groovy おめでとうございます! 今年も Groovy にずいぶん助けてもらいました。 🙂

Keep on Groovy-ing!