フロントエンド技術を導入した 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 開発者ツールのファイル監視&自動リロード処理が動かないケースがあります)

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 を指定して使っています)

さらに、このアプリケーションで使われている 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 開発環境内で同時にサポートされますので、楽しくモダンなフロントエンドを学べる良い環境になるのではないかと思います。

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

Grooscript で Canvas アニメーション

G* Advent Calendar 2015 16日目です!(更新前に来てくださった方すいません…)


Groovy を altJS にしてしまった驚異の Grooscript を紹介したいと思います。Groovy をかくとソースコードが JavaScript に変換され、ブラウザや node.js 上で実行できてしまいます。

論よりrunということで、Try online がありますので試してみると良いと思います。

Try groovy to javascript conversion with grooscript

http://grooscript.org/conversions.html

ここでは、ブラウザを Grooscript の実行環境とし、Groovy で Canvas を使ったアニメーションのプログラミングをしてみます。

Grooscript を始めるには Grooscript の Gradle plugin を使うと簡単です。初期コードの作成からファイルウォッチからの変換まで可能です。

build.gradle

plugins {
    id "org.grooscript.conversion" version "1.2.2"
}

これで、

gradle initStaticWeb

とすれば、初期コードを生成してくれます。

grooscript-01

この状態で次のようにすれば、src/main/groovy 配下の *.groovy ファイルの更新を監視して、src/main/webapp/js/app の下に変換した *.js を入れてくれます。

gradle --daemon convertThread

監視を止める場合は gradle のデーモンを停止させればOKです。(Windows, Linux, OS X とも動作するはずです)

gradle --stop

あとは Groovy をかけば完成です。 🙂

ここでは Canvas アニメーションを実装するために、src/main/webapp/index.html を次のようにしました。

<html>
<head>
<title>groovy on browser!</title>
<link rel="stylesheet" href="css/style.css" />
<script type="text/javascript" src="js/lib/grooscript.min.js"></script>
<script type="text/javascript" src="js/app/Presenter.js"></script>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<canvas></canvas>
</body>

src/main/groovy/Presenter.groovy で Canvas に対するアニメーション処理を Groovy でかきました。

ブラウザの window や document などのオブジェクトはそのまま記述できます。

import org.grooscript.asts.GsNative

class Animation {

    static ANIMATION_FPS = 60;

    def canvas
    def context
    def width
    def height

    def actor
    def line

    Animation() {
        window.addEventListener('load', init, false)
        window.addEventListener('resize', init, false)
        window.addEventListener('orientationchange', init, false)
        window.setInterval(update, 1000 / ANIMATION_FPS, false)
        render()
    }

    def init() {
        actor = []
        canvas = document.getElementsByTagName('canvas')[0]
        context = canvas.getContext('2d')
        width = canvas.clientWidth
        height = canvas.clientHeight
        canvas.setAttribute("width" ,width);
        canvas.setAttribute("height" ,height);
        for(def degree = 0 ; degree <= 360; degree += 360/86) {
            this.actor << new Ham("images/ham.png"
                , this.width
                , this.height
                , degree
            )
        }
    }

    def update() {
        actor.each {
            it.update()
        }
    }

    def render() {
        window.requestAnimationFrame(render)
        if(!context) return
        context.fillStyle = "#000"
        context.fillRect(0, 0, width, height)
        actor.each {
            it.draw(context)
        }
    }
}

class Ham extends Actor {

    def swidth
    def sheight
    def degree
    def sx
    def sy
    def sv

    Ham(def url, def w, def h, def d) {
        super(url)
        swidth = w
        sheight = h
        sx = swidth / 2
        sy = sheight / 2
        sv = 1
        degree = d
        width = 48
        height = 48
    }

    @GsNative
    def update() {/*
        var radian = Math.PI / 180 * this.degree
        this.x = Math.cos(radian) * (this.sx / 2 - 24) + this.swidth / 2 - 24
        this.y = Math.sin(radian) * (this.sy / 2 - 24) + this.sheight / 2 - 24
        this.degree += 0.9
        if(this.degree > 360) {
            this.degree = 0
        }
        this.sx += this.sv * 8
        this.sy += this.sv * 8
        if(this.sx >= this.swidth
            || this.sy >= this.sheight
            || this.sx <= (this.swidth * -1) + 48 + 24
            || this.sy <= (this.sheight * -1) + 48 + 24
            ) {
            this.sv = this.sv * -1
        }
        this.width = 48 + (this.sx / 48)
        this.height = 48 + (this.sy / 48)
    */}
}

abstract class Actor {

    def image
    def x = 0
    def y = 0
    def width
    def height

    Actor(def url) {
        image = Resource.getInstance().getImage(url)
    }

    abstract update()

    @GsNative
    def draw(context) {/*
        if(!this.image['loaded']) return
        context.drawImage(this.image['image'], this.x, this.y, this.width, this.height)
    */}
}

class Resource {

    def static resource = new Resource()
    def images = [:]

    private Resource() { }
    
    def static getInstance() {
        return resource
    }

    def getImage(url) {
        if(!images.containsKey(url)) {
            images[url] = [:]
            def image = document.createElement('img')
            image.src = url
            image.addEventListener('load', {
                images[url]['image'] = image
                images[url]['loaded'] = true
                images[url]['width'] = image.naturalWidth
                images[url]['height'] = image.naturalHeight
            }, false)
        }
        return images[url]
    }
}

new Animation()

本当に Groovy です。 😀

一部 @GsNative というアノテーションがついたコメント実装されているメソッドは JavaScript のネイティブコードです。 速度を稼ぎたい部分は、このように(Groovy で変換された周りのコードを考えつつ) JS をそのままかくこともできます。

以下をクリックすると、実際に動いている様子が見ることができます。

ham

変換された JavaScript のコードを見ると面白いと思います。

Grooscript はここ1年で node.js、require.js や React.js の対応が進むなど、開発を見ているのも楽しいです。

ブラウザとのつなぎも簡単ですので、Groovy な方はぜひ試してみてください 🙂

Keep on Groovy-ing!

JavaScript デバッグ環境の用意(JSDT + Firebug + Crossfire)

前回に引き続きまして JavaScript の開発環境構築です。 今回は、少し大きなプログラムを作り始めるとなくてはならないデバッガを設定してみます。

JavaScript のデバッグ環境は、IE の開発者ツールや Firefox の FIrebug などでも準備できますが、ここでは Eclipse WTP/JSDT の環境のビューから直接ブレイクやウォッチをする方法をかいてみます。

WTP/JSDT についてはこちらです。

JavaScript 開発環境の用意(Eclipse WTP/JSDT) | hiromasa.another :o)

Eclipse に含まれる Web Tool Platform(WTP)には、JavaScript Development Tools(JSDT)が含まれており、おそらくみなさんがインストールしているであろう、Eclipse for  JavaEE Developer に最初から含まれています。

JSDT からブラウザの JavaScript にリモートアタッチしてデバッグする方法はいくつかあるようですが、JSDT の標準インストールで FIrebug / Crossfire プロトコルに接続して行うパッケージが入っていますので、こちらを用いてみました。

JSDT/Debug/Crossfire – Eclipsepedia

Support for remote Firebug using the Crossfire protocol is available in the JSDT development bundles and is provided to allow remote debugging of JavaScript using Firebug via Crossfire.

まず、Firebug / Crossfire を使うために Firefox に Firebug と Crossfire アドインをインストールします。 Crossfire の .xpi についてはこちらです。(下の方に .xpi へのリンクあり)

mrennie/crossfire · GitHub

Crossfire is a Firebug extension which implements a JSON protocol to allow remote clients (like an IDE or code editor) to connect to Firebug.

Firefox にアドインがインストールできたら、Eclipse から接続する準備を行います。Crossfire アドインのスタートがちょっとわかりにくいのですが、FIrefox の表示 -> ツールバー -> アドオンバーを表示して、右下のアイコンからスタートします。

130322-0001

アタッチポートを聞かれてくるので、ここではそのまま 5000 で起動します。

130322-0002

でもって、FIrefox でデバッグしたいサイトを開いて Firebug で一度「スクリプト読み込み」しておきます(これが重要)。ここでは Eclipse の jstest プロジェクトに配置された、http://127.0.0.1/jstest/index.html を開きました)

130322-0003

次に Eclipse 側のデバッグ構成をします。 デバッグ構成からリモート JavaScript に jstest 構成を追加します。 コネクターは Crossfire – Remote “Attach” を選択し、ソースからソースコードをマッピングします。

130322-0004

130322-0005

このステップで準備完了です。 そのまま右下デバッグボタンを押下すると、Firebug に接続され、先のアイコンが接続状態になり、Eclipse のデバッグプロセスも起動します。

130322-0007

何かデバッガの挙動がおかしいなと思ったらここから再起動してあげてください。なおります。(ブラウザとの通信状態によってブレイクポイントの位置などが不整合することがあるようです)

130322-0006

あとは通常の Eclipse のデバッグ操作です。 ソースコードにブレイクポイントをはってみます。 F6, F7, F8 キーなどでステップ実行やウォッチが可能です。

130322-0008

ブレイク中のウォッチはカーソルあてればホバーしてきます。

ふむ。

130322-0009

ほう。

130322-0010

ほほう。

130322-0011

変数ビューを使えば、Firebug から送られてくる生のブラウザの状態もみれます。

130322-0012

これを見ているだけでもブラウザの内部動作の理解ができますね。

この手順で Eclipse から Firebug にいったん接続すれば、あとはそっとしておいて大丈夫です。作業開始時にでもつないでおくといいでしょう。

機能的には Firebug でも当然同じことが出来ますが、Eclipse からであればそのまま見やすいソース表示でデバッグブレイクでき、変数ウォッチなどもビューを使って操作しやすいかなと思いました。 🙂

プログラムのなぞの挙動は、考えるよりデバッガひっかけたほうが解決簡単。論より run が座右の銘のひろましゃでありました。がってんがってん。(←調子が悪いとオチが弱くなるらしい・・・