CLOVER🍀

That was when it all began.

xsbt-web-plugin 0.9.0でServlet&JSPプログラミング

バージョンが上がると使用するコマンドが変わる印象のあるxsbt-web-pluginですが、特にソースコードの自動反映とかを忘れがちなので、今一度まとめてみることにしました。

xsbt-web-plugin
https://github.com/earldouglas/xsbt-web-plugin

xsbt-web-plugin 0.1の頃から考えると、これで3度目な気がします…。

使用する前庭の環境は、以下とします。

ソフトウェア名 バージョン
Scala 2.11.0
sbt 0.13.2
xsbt-web-plugin 0.9.0
Jetty* 9.1.5.v20140505
Embedded Tomcat 7.0.53

*xsbt-web-pluginのコンテナ用。どちらかを使用します

また、sbtの設定はBasic Configurationとします。

build.sbtとplugins.sbtの設定

まず、plugins.sbtには以下の記述をしておきます。
project/plugins.sbt

addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "0.9.0")

続いて、build.sbt。
build.sbt

name := "hello-servlet"

scalaVersion := "2.11.0"

// xsbt-web-pluginの設定
seq(webSettings :_*)

// WARファイル名から、バージョンの表記を省くための設定
artifactName in packageWar := {
  (scalaVersion: ScalaVersion, module: ModuleID, artifact: Artifact) =>
    artifact.name + "." + artifact.extension
}

// 依存関係の設定

普段ならscalacOptionsとか書くところですが、今回はすべて端折りました。

依存関係の部分は、Jettyを使う場合は

val jettyVersion = "9.1.5.v20140505"

libraryDependencies ++= Seq(
  "org.eclipse.jetty" % "jetty-webapp" % jettyVersion % "container",
  "org.eclipse.jetty" % "jetty-plus"   % jettyVersion % "container",
  // JSPを使う場合は、以下を追加
  "org.eclipse.jetty" % "jetty-jsp"   % jettyVersion % "container",
  // Jetty 9は、Servlet 3.1の実装
  "javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided"
)

Embedded Tomcatを使う場合は

val tomcatVersion = "7.0.53"

libraryDependencies ++= Seq(
  "org.apache.tomcat.embed" % "tomcat-embed-core"         % tomcatVersion % "container",
  "org.apache.tomcat.embed" % "tomcat-embed-logging-juli" % tomcatVersion % "container",
  "org.apache.tomcat.embed" % "tomcat-embed-jasper"       % tomcatVersion % "container",
  // Tomcat 7は、Servlet 3.0の実装
  "javax.servlet" % "javax.servlet-api" % "3.0.1" % "provided",
  // JSTLを使う場合
  "javax.servlet" % "jstl" % "1.2"
)

となります。xsbt-web-pluginはTomcat 8に対応していないので、Tomcat 7を使用しています。

なお、Tomcat 7はServlet 3.0の実装なので、厳密にはJetty 9と同じにならないですね…。xsbt-web-pluginのドキュメントは、Jettyを使っていてもServlet 3.0の指定になっていますが。

今回、JSPJSTLを使用したのですが、そのケースにおいて必要なJARはそれぞれコメントで書いています。Jettyの場合はJSPを使うために「jetty-jsp」への依存関係が必要でJSTLの設定は特になくても動作しました。Embedded Tomcatの場合は、JSTLへの依存関係を別途追加する必要があります。

今更JSP?という話は、さておき…。

ServletJSPの作成

動かしてみるために、こんなServlet
src/main/scala/example/HelloServlet.scala

package example

import java.io.IOException

import javax.servlet.ServletException
import javax.servlet.http.{HttpServlet, HttpServletRequest, HttpServletResponse}

class HelloServlet extends HttpServlet {
  @throws(classOf[IOException])
  @throws(classOf[ServletException])
  override protected def doGet(req: HttpServletRequest, res: HttpServletResponse): Unit = {
    val message = "Hello Servlet!"

    req.setAttribute("message", message)

    req
      .getRequestDispatcher("/WEB-INF/view/hello.jsp")
      .forward(req, res)
  }
}

JSPを作成してみます。
src/main/webapp/WEB-INF/view/hello.jsp

<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
  <head>
    <title>Hello Servlet &amp; JSP</title>
  </head>
  <body>
    <h1>はじめてのServlet &amp; JSP</h1>
    <p><c:out value="${message}" /></p>
</html>

そして、web.xml
src/main/webapp/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">
  <servlet>
    <servlet-name>HelloServlet</servlet-name>
    <servlet-class>example.HelloServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>HelloServlet</servlet-name>
    <url-pattern>/hello</url-pattern>
  </servlet-mapping>
</web-app>

Servlet 3.0以上なら、アノテーション付ければweb.xml要らなくない?という話もあるかと思いますが、xsbt-web-pluginではうまく動かないようです。というか、認識しませんでした。

起動と停止

コンテナとなっている、JettyまたはTomcatの起動は以下のコマンドで、

> container:start

停止は以下のコマンドで行います。

> container:stop

Commands
https://github.com/earldouglas/xsbt-web-plugin/wiki/Commands

リロード

ソースコードの変更を反映するには、

> compile

した後にリロードします。

> container:reload /

ここでの「/」はコンテキストパスで、省略するとエラーになります。

また、container:reloadはコンパイルを行ってくれるわけではないので、あらかじめコンパイルが必要です。

オートリロード

上記のリロードの手順が面倒、またはソースコードの変更を自動で判定して欲しい場合は、以下のようなコマンドを仕込みます。

> ~;container:start; container:reload /

もしくは

> ~;copyResources; auxCompile

もしくは

> ~;compile; container:reload /

など。

このうち、以下はTomcatではコンパイルはしてくれますが、動作中のWebアプリには反映されませんでした…。

> ~;copyResources; auxCompile

WARファイルを作る

packageコマンド

> package

または、packageWarコマンドで。

> packageWar

この時、アーティファクトScalaバージョンを入れたくない場合は、

// WARファイル名から、バージョンの表記を省くための設定
artifactName in packageWar := {
  (scalaVersion: ScalaVersion, module: ModuleID, artifact: Artifact) =>
    artifact.name + "." + artifact.extension
}

のように設定してくださいね。

最近まで使っていなかったのですが、使う理由ができた時にコマンドをいろいろ忘れていたので、これを機にまとめなおしです。