はじめに

Java/Spark プロジェクトの Gradle を利用したビルド/デプロイについて、私が試したことをまとめたものです。 (ここで言うSparkは、分散処理ライブラリのApache Sparkではなく、Java の Sinatra ライクな Microframework のSparkのことです)

目次

  1. Hello World アプリ
  2. jade テンプレートエンジン
  3. Grunt/bower による Web パッケージのビルド
  4. 付録 A. TravisCI によるビルド
  5. 付録 B. Gradle で docker build
  6. 付録 C. TravisCI から DockerHub へ docker push

各章でのソースコードは Commits · kaakaa/gradle-frontend-boilerplate から確認できます。

環境

gradle-frontend-boilerplate

3. Grunt/bower による Web パッケージのビルド

概要

Gradle のビルドフローにnpm,grunt,bowerを組み込む。

厳密には、Gradle プラグインcom.moowork.gruntを使ってnpm installと Grunt タスクを実行するだけ。
bowerは Grunt タスクとして操作する。


Java プロジェクトから bootstrap などのフロントエンド資産を利用する場合、WebJars - Web Libraries in Jarsを利用するなどの方法もあるが、今回はフロントエンドツールに慣れるために、なるべくフロントエンド部分は Gradle から実行するだけにしたかったからである.

実行

git clone https://github.com/kaakaa/gradle-frontend-boilerplate.git
cd gradle-frontend-boilerplate
git checkout sec_3

./gradlew run

ビルドスクリプト

plugins {
    id 'java'
    id 'application'
    id 'com.moowork.grunt' version '0.11'  // (1) - npm/gruntを操作するGradleプラグインを指定する
    id 'com.moowork.node' version '0.11'
}

group 'org.kaakaa'
version '1.0-SNAPSHOT'

/** For Java build */
sourceCompatibility = '1.8'
targetCompatibility = '1.8'

mainClassName = 'org.kaakaa.spark.Main'

sourceSets {  // (2) - フロントエンドビルドの成果物をJavaビルドのリソースに組み込む
    main {
        resources {
            srcDir 'src/main/resources'
            srcDir 'src/main/web/bower_components'
        }
    }
}

repositories {
    mavenCentral()
}

dependencies {
    compile 'com.sparkjava:spark-core:2.3'
    compile 'com.sparkjava:spark-template-jade:2.3'
}


/** For frontend build */
node {  // (3) - nodeの設定
    version = '4.4.0'
    download = true
    workDir = file("${projectDir}/src/main/web")
    nodeModulesDir = file("${projectDir}/src/main/web")
}

grunt {
    workDir = file("${projectDir}/src/main/web")
}

grunt_build.dependsOn 'installGrunt'  //  (4) - gruntの実行環境を構築する
grunt_build.dependsOn 'npmInstall'
processResources.dependsOn grunt_build

/** Other settings */
task wrapper(type: Wrapper) {
    gradleVersion = '2.12'
}

(1) npm/grunt を操作する Gradle プラグインを指定する

com.moowork.gruntプラグインでは Grunt をインストールするinstallGruntタスクと、Grunt のタスクを実行するgrunt_<task>ルールが Gradle タスクとして追加される。 今回はgrunt_buildタスクの実行により grunt のbuildタスクを実行することを想定している。

また、com.moowork.nodeプラグインで、node.js/npm のインストールと node/npm タスクの実行を Gradle から行うことができる。

(2) フロントエンドビルドの成果物を Java ビルドのリソースに組み込む

今回、フロントエンド系の資産についてはsrc/main/web内で扱うようにしていますが、そこで生成された js/css ファイルを Java ビルドの成果物に含めるためにsrc/main/web/bower_componentsを resources フォルダとして追加しています。

(3) node の設定

com.moowork.nodeで使用する設定をnode節に、com.moowork.gruntで使用する設定をgrunt節にそれぞれ記述しています。 先にも述べたように、node/grunt で扱う資産はsrc/main/web内で扱うため、workDir にそのディレクトリを指定しています。

(4) grunt の実行環境を構築する

grunt_build タスクが実行される前にinstallGrunt,npmInstallが実行されるよう設定しています。 また、grunt_buildタスクが、Java ビルドのリソースを処理するprocessResourcesタスクの実行前に実行されるよう設定しています。

ビルドスクリプト(Grunt)

Gradle のgrunt_buildタスクにより実行される grunt ファイル. src/main/web/Gruntfile.jsに置いてある.

module.exports = function(grunt) {
  grunt.initConfig({
    "bower-install-simple": {  // (1) - bower installを実行する
      options: {
        directory: "bower_components/public"  // (2) - bower installの出力先を変更する
      },
      "prod": {
        options: {
          production: true
        }
      },
      "dev": {
        options: {
          production: false
        }
      }
    },
    wiredep: {
      task: {
        src: ['../resources/templates/**/*.jade'],  // (3) - jadeファイルにbowerコンポーネントへの参照をinjectする
        directory: 'bower_components/public',
        ignorePath: '../../web/bower_components/public'  // (4) - bowerコンポーネントへの参照パスから要らない部分を削除する
      }
    }
  });

  grunt.loadNpmTasks('grunt-bower-install-simple');
  grunt.loadNpmTasks('grunt-wiredep');

  grunt.registerTask('default', 'build');
  grunt.registerTask('build', ['bower-install-simple', 'wiredep']);
};

(1) bower install を実行する

bower installを実行する. 今回、bower.json にはbootstrapへの依存のみ宣言してある.

(2) bower install の出力先を変更する

bower installの出力先がbower_componentsままだと、bower のコンポーネントを Spark で Static ファイルとして配信出来ない(ココらへんは Gradle のprocessResourceタスクとの関係). とりあえず今回はbower installの出力先を変更し、Spark の Static ファイルを/publicに指定することにしている.

(3) jade ファイルへ bower コンポーネントへの参照を inject する

bower installで install されたコンポーネントへの参照を jade ファイルへ記述するタスク. 便利なのだが、grunt-wiredepはもうメンテされてないらしい. stephenplusplus/grunt-wiredep: Inject Bower packages into your source code with Grunt.

(4) bower コンポーネントへの参照パスから要らない部分を削除する

(3) jadeファイルへbowerコンポーネントへの参照をinjectするで jade ファイルに inject されるパスは、相対パスのため../../web/bower_components/public/bootstrap/dist/css/bootstrap.cssというような形式になる. このままでは Web サーバとして動作させた時に上手く動かないため、不要な部分を削る.

この削り方は(2) bower installの出力先を変更するに依存しており、ここらへんの操作のコンテキストがとても高くなりがちなのが悩みどころ.

アプリケーションコード

package org.kaakaa.spark;

import spark.ModelAndView;
import spark.template.jade.JadeTemplateEngine;

import java.util.Collections;

import static spark.Spark.get;
import static spark.Spark.port;
import static spark.Spark.staticFileLocation;

public class Main {
    public static void main(String[] args) {
        port(8080);
        staticFileLocation("/public");  // (1) - Staticファイルの格納場所として`/public`を指定する

        get("/hello", (rq, rs) -> new ModelAndView(Collections.EMPTY_MAP, "hello"), new JadeTemplateEngine());
    }
}

(1) Static ファイルの格納場所として/publicを指定する

ビルドスクリプト(Grunt)の(2) bower installの出力先を変更するでも触れたように、Spark の Static ファイルの格納場所として/publicを指定する.

アプリケーションコード(jade)

doctype html
html(lang="ja")
  head
    title gradle-frontend-boilerplate
    // bower:css  // (1) - grunt-wiredepタスクによりbowerのmainコンポーネントがinjectされる
    // endbower
    // bower:js
    // endbower
  body
    nav.navbar.navbar-inverse.navbar-fixed-top
      .container
        .navbar-header
          a.navbar-brand Gradle-frontend-boilerplate
    .container(style="margin-top: 50px;")
      h1 gradle-frontend-boilerplate
      p Hello World!

(1) grunt-wiredep タスクにより bower の main コンポーネントが inject される

grunt のgrunt-wiredepタスクが実行されると、bower の main コンポーネントとして指定されている js/css ファイルへの参照がコメントの間に inject される。

comments powered by Disqus