Web Flavor

[English] [日本語]

トップ スクリーンショット マニュアル ダウンロード Wiki チャット

1 概要 2 使い方 3 ダウンロード 4 ライセンス 5 連絡先 6 最後に Appendix

Version 0.3.0a2
2008.10.22

1 概要

1.1 はじめに

ScalaでWebアプリケーションを簡単に作れないか調べていましたが、なかなかいいものがありません。一方JSPのScriptletを使えば様々な問題こそあれ、かなりハードルを下げることができます。

そこでJSPのコンセプトを取り入れ、

という機能を備えたWebフレームワークを作ってみました。

以前のWeb Flavor version 0.1は、Rhino(JavaScript)やJRuby(Ruby)などでも動きましたが、今回はScalaだけに特化し、Scalaの特徴を生かせるよう作られています。

Better CGI, Better PHP!

強く型にはめず、CGIやPHPのように手軽に、そして弱い枠組みで(かつ十分)Webアプリケーションを作れる環境を目指しています。

1.2 特徴

Web Flavorは以下の特徴をもっています。

2 使い方

2.1 動作環境

以下の環境を用意してください。

Java Java 5 Runtime Environment以上
Java Servlet Java Servlet 2.3以上
Java Servletコンテナ Apache Tomcat 6以上(Tomcat 5以降でも動作すると思われる, Jettyなど他のコンテナは未確認)
Scala 2.7.1.final (標準添付)

全環境を試していないので、動作しない環境があるかもしれません.

2.2 インストール

${WEB_FLAVOR_HOME}/webflavor.warをServletコンテナに配置(デプロイ)してください。

なお、Servletコンテナによっては正常に動作しない可能性があるので、${WEB_FLAVOR_HOME}/lib/scala-library.jarをServletコンテナのCLASSPATHに加えてください。

2.3 Flavorとは

Flavorとは、Scalaの文法で書かれるWebアプリケーションのスクリプトです。

Web Flavorでは、Flavorを記述することによってWebアプリケーションを開発します。

詳細は、「A3 Flavorとは」をご覧ください。

2.4 Flavorソースの配置とアクセスパスのマッピングルール

作成したFlavorソースは、WEB-INF/src/下に置かれ、その相対パスがそのままアクセスパスにマッピングされます。

例えば、webflavorというディレクトリに配置した場合、

Flavorソース URL
WEB-INF/src/examples/Foo.scala http://localhost:8080/webflavor/examples/Foo.scala

になります。

詳細は、「A4 Flavorソースの配置」と「A5 アクセスパスとFlavorソースのマッピングルール」をご覧ください。

2.5 API

Scalaから使いやすいようにJava Servlet APIをラッピングして提供されており、Java Servletと同じ要領で使うことができます。

詳細は、「A2 APIリファレンス」をご覧ください。

2.6 プログラミング

具体的な例を示しながら説明します。

2.6.1 Hello, world!

たとえば、"Hello, world"を表示するFlavorを作ります。

// HelloWorld.scala

// タイトル名
val TITLE = "Hello, world!"

// Web Flavorエンジンに返されるXML要素
// レスポンスとして出力されます
<html><body><h1>{TITLE}</h1></body></html>

ファイル名をHelloWorld.scalaとしてWEB-INF/srcに保存してください。文字コードはUTF-8です。

このソースは単なるテンプレートのように見えますが、そうではなく、これはScalaのコードになっています。

返り値を明示的に書いていないので、解りにくいかもしれませんが、最後の行は、

return <html><body><h1>{TITLE}</h1></body></html>

と等価です(Scalaではよくreturn文が省かれます)。

XML要素を返していますが、Stringやnullなどを返しても構いません。

2.6.2 Echo

リクエストしたパラメータなどを表示するFlavorを作ります。

// Echo.scala

// タイトル名
val TITLE = "Echo"

// Web Flavorエンジンに返されるXML要素
// レスポンスとして出力されます
<html>
<head>
<title>{TITLE}</title>
</head>
<body>
<h1>{TITLE}</h1>
<h2>Headers:</h2>
{for ((name, value) <- request.headers) yield <p>{name}: {value}</p>}
<h2>Parameters:</h2>
{for ((name, value) <- request.params) yield <p>{name}: {value}</p>}
</body>
</html>

ファイル名をEcho.scalaとしてWEB-INF/srcに保存してください。

requestはHttpServletRequest相当のインスタンスで、ヘッダー情報であるrequest.headers: Map[String,String]と、パラメータ情報であるrequest.params: Map[String,String]も用意されています。

それぞれSeq[String,String]であるので、そのままfor文でループを回して中身を得ることができます。

2.6.3 POHPテンプレート

0.2.7からPOHP(Plain Old HTML Page)によるページ生成ができるようになりました。

この機能は、HTMLテンプレートを導入することでViewを分離でき、またPure HTMLをテンプレートにすることで、Webデザイナとの協業がしやすくなります。

まず最初に "src/POHPHelloWorld.html"(UTF-8) としてHTMLテンプレートを作成します。

<html>
<head>
<title><span flavor:id="title">title</span></title>
</head>
<body>
<h1><span flavor:id="title"/></h1>
<span flavor:id="message">Hello</span>
</body>
</html>

<span flavor:id="リソース名"/>要素が埋め込むリソースになります。

次にFlavorを "src/POHPHelloWorld.scala"(UTF-8) として作成します。

Template("",
  "title" -> Text("Hello, world!"),
  "message" -> <p>Hello, POHP world!</p>
)

Template("ページ名", (String,Seq[scala.xml.Node])*) は、テンプレートオブジェクトを作成するメソッドです。

"title" -> Text(...) は、挿入するリソースを示しています。リソースは、Seq[scala.xml.Node]で、XML要素(XMLリテラルなど)をそのまま使えます。テキストの場合はTextメソッドを使ってXML要素に変換してください。

またテンプレートファイルとFlavorソースファイルは同じディレクトリに同じファイル名(拡張子はそれぞれ".html"と".scala")で置くようにしてください。

これだけで、リソース部分を書き換えたHTMLページを出力できます。

<html>
<head>
<title>Hello, world!</title>
</head>
<body>
<h1>Hello, world!</h1>
<p>Hello, POHP world!</p>
</body>
</html>

なお、同じURLのFlavorで複数の異なるページを返してやることができます。

"src/POHPHelloWorld.scala"で、

Template("Foo",
  "title" -> Text("Hello, world!"),
  "message" -> <p>Hello, POHP world!</p>
)

とした場合、対象となるテンプレートは"src/POHP_HelloWorld_Foo.html"となり、ページ名と名前の組み合わせにより、複数ページを返すことが可能になります。

なお、デフォルトのページ名は""(0文字)です。

POHPテンプレートの詳細については「A9 POHPテンプレート」をご覧ください。

2.6.4 注意点

Context#attributesやSessionなど永続化されるオブジェクトの型をFlavorのソース内で定義し、Flavorソースを更新した場合、ClassCastExceptionが発生する可能性があります。

これは永続化されたオブジェクトと新しく作成したクラスのクラス空間(ClassLoader)が異なる為で、Servletコンテナを再起動する、あるいは、DeployしなおすまでClassCastExceptionが発生しつづけます。

いくつか解決策はあるのですが、

などの対策を行ってください。

2.6.5 動的ライブラリ

動的ライブラリとは、Flavorで使われるライブラリで、Flavorとともにコンパイルされ、同じClassLoader上で動かす事ができます。

動的ライブラリを使うことでFlavorで使われるコードを共有でき、まだ動的にコンパイルされて使われるため、試行錯誤をしながら開発を進めていくことができます。

詳しくは「A10 動的ライブラリ」をご覧ください。

2.6.6 Flavor Compiler

0.2.5から、Flavorを事前にコンパイルしてWeb Applicationとしてパッケージ化できるようになりました。

特徴としては、

と軽量にでき、Flavorで開発したプログラムをパッケージ化して運用するのに適しています。

開発手順としては、

  1. Flavorに必要なライブラリを作成しコンパイルする
  2. Flavorソースを作成する
  3. flavorcコマンドまたはflavorc Antタスクを利用してFlavorをコンパイルする
  4. Flavorクラスやライブラリなど必要なクラスをパッケージ化する(jar化, war化)
  5. Servletコンテナに配置する

となります。

flavorcコマンドやflavorc Antタスクについては、「A8 Flavor Compiler」をご覧ください。

なお、Servletコンテナに配置する前に、web.xmlを以下のように書き直してください。

  <filter>
    <filter-name>WebFlabor</filter-name>
    <filter-class>
      jp.ne.cappuccino.keisuken.servlet.flavor.WebFlavorFilter
    </filter-class>
...
    <init-param>
      <param-name>staticMode</param-name>
      <param-value>true</param-value>
    </init-param>

staticModeをtrueにすると、Flavorは静的に動作するようになり、動的にコンパイルされなくなります。

2.6.7 その他

WEB-INF/srcディレクトリのサンプルソースもご覧下さい.

2.7 Administrator menu

0.2.0a3から新しく Administrator menu を設けました。

これは、Web Flavorを管理・操作する為のメニューで、現在は、

が行えるようになっています。

この機能により、Web FlavorをWebブラウザからFlavorソースコードを編集して実行できるため、Web Flavorのハードルを下げ、開発サイクルをあげることが出来ます。

またAministrator menu自体もFlavorで書かれています。

詳細については、「A7 Administrator menu」をご覧ください。

2.8 デモンストレーションパッケージについて

WebFlavor-samples-x.x.x.zipをダウンロードして適当なディレクトリに展開し、環境変数としてJAVA_HOME または JRE_HOMEにJavaのランタイムのホームディレクトリ(ex. C:\Program Files\Java\jre1.6.0_07)を設定した後、WebFlavor-samplesのstartup.batまたはstart.shを実行すれば、サンプルアプリケーションが起動します。

http://localhost:8080/にアクセスすれば、Web Flavorのサンプルアプリケーションを実行できます。

また、Administrator menuは、BASIC認証してありますので、ユーザ名: webflavor , パスワード: webflavor でログインしてください。

3 ダウンロード

以下のページからダウンロードできます.

https://sourceforge.net/project/showfiles.php?group_id=242794

アーカイブ 詳細
WebFlavor-#.#.#.zip ソースとWARファイルパッケージ
WebFlavor-samples-#.#.#.zip サンプルアプリケーション(Apache Tomcat込)

以下のページからダウンロードできます.

4 ライセンス

ライセンスファイルを参照してください.

なお,このソフトウェアを使って生じた損害などについては,作者である西本 圭佑は一切責任をとりませんのでご了承ください.

また,Scalaなどのランタイムについても,各ライセンスファイルをご覧下さい.

5 連絡先

質問や要望などがありましたら keisuken atmark cappuccino.ne.jp あるいは Twitterの @keisuke_n まで内容を書いてお送りください.

6 最後に

Administrator menuでソースの生成から編集までできるようになり、ちょっとしたCGIぽいものをServletコンテナ上で試すことができ、Scalaの記述性も助けて、便利に使えるものになったと思います。

パフォーマンスも、コンパイルに時間がかかることに目をつぶれば、生のJava Servletと同じくらいのパフォーマンスが出ると思います。

JSPのScriptletでも短いものは同様に軽快に書けていけますが、JSPの記述ルールをある程度覚えなくてはいけなかったり、XMLのタグの中にコードが埋もれてしまって、読みにくく書きにくいものになっています。

Web FlavorではScalaのコードのみ許すことによって、読みやすさや書きやすさを追求できますし、XMLリテラルがあるために、Viewをかなり簡単に記述できます。XSS(クロスサイトスクリプティング)が生じにくくなる良い副作用もあります。

通常ViewとControllerを混ぜて記述するのは推奨されせんが、Web FlavorではViewとController、場合によってはModelまで1つのソースファイルに書いてしまいます。しかし「それでもいい」と思わせてしまう魅力を持っていると思います。

しかしまだ未完成です.APIなどの仕様が大幅に変更される可能性がありますし,バグもたくさんあります、ドキュメントもまだまだ十分とはいえませんが、これからも改良していきたいと思います。応援してください。

Appendix

A1 動作環境

以下の環境を前提にしています。

Java Java 5 Runtime Environment以上
Java Servlet Java Servlet 2.3以上
Java Servletコンテナ Apache Tomcat 6以上(Tomcat 5以降でも動作すると思われる, Jettyなど他のコンテナは未確認)
Scala 2.7.1.final (標準添付)

A2 APIリファレンス

APIリファレンス

Flavor API

Flavor APIは、Flavorを書くために必要なリソースや設定などを操作する為のAPIです。

Flavor GUI(FORM) API

Flavor GUI APIは、FlavorでFORMなどを書くときに使用する一種のフレームワークになっています。

主に、GUIコンポーネント(入力項目の表示と入力値の保持)とValidator(入力値のチェック)に分かれます。

A3 Flavorとは

Flavorとは、Scalaの文法で書かれるWebアプリケーションのスクリプトです。

A3.1 作成ルール

以下のルールで作成します。

A3.2 Flavorソース

Web FlavorでのFlavorのソースは、UTF-8で記述します(固定)。

Flavorのソースは以下のように、

を継承したクロージャ(クラス)になっていて、context, request, response, sessionなどの変数はクロージャの引数として渡されます。

またクロージャ(クラス)の記述はWeb Flavorエンジンが自動的に付加してくれるため、クロージャの中身だけ記述すればいいようになっています。

import jp.ne.cappuccino.keisuken.servlet.flavor.{Context,Request,Response,FlavorUtils}
import jp.ne.cappuccino.keisuken.servlet.flavor.HTMLUtils._
import java.io._

class {名前} extends (Context,Request,Response,Session) => AnyRef {
  def apply(
    context: Context,
    request: Request,
    response: Response,
    session: Session): AnyRef = {
    val __flavor_util__ = new FlavorUtils(context, request, response, session)
    import __flavor_util__._
// ここにFlavorのソースが記述される
  }
}

返り値はそのままServletのレスポンスオブジェクトとして扱われ、

返り値 出力
null 何もしない
String 文字列
scala.xml.Node(XMLリテラルなど) XML/(X)HTML
Template(POHPテンプレート) XML/(X)HTML
Book(null/String/scala.xml.Node/Temlateなど) 何もしない/文字列/XML/(X)HTML

として出力されます。

また出力は、

によって設定でき、例えば

response.contentType = "text/plain; charset=Windows-31J"

とすれば、テキスト形式で文字エンコードはWindows-31Jとして扱われます。

基本的な手順は、

で、レスポンスオブジェクトを返す為のコードを記述することになります。

A3.3 HTML 4.01およびXHTML 1.0に準じたHTMLの出力

ScalaのXML要素はテンプレート的な使い方を始めかなり便利に使うことができますが、(X)HTMLに準拠したHTMLページを出力することができません。

そこで、response.documentTypeにHTMLタイプを代入すれば、XML要素で作ったHTMLを出力すれば(X)HTMLに準じたHTMLを出力できます。

HTML定数は、

定数 (X)HTML/XMLタイプ
HTML_4_01_STRICT HTML 4.01 Strictタイプ
HTML_4_01_TRANSITIONAL HTML 4.01 Transitionalタイプ
XHTML_1_0_STRICT XHTML 1.0 Strictタイプ
XHTML_1_0_TRANSITIONAL XHTML 1.0 Transitionalタイプ
XML_1_0 XML 1.0

があります。

以下にサンプルを示します。

// 各種処理
...

// HTMLタイプの設定 (XHTML 1.01 Transitional)
response.documentType = XHTML_1_01_TRANSITIONAL

// 出力するXMLオブジェクト
<html>
<body>
</body>
</html>

Response#documentTypeに設定すると、

をします。html要素などは、XML要素に示された通り出力しますので、xmlns属性やxml:lang属性などは開発者が定義します。

A3.4 暗黙のメソッド

Flavorソース中では、

のクラスがインスタンス化されimportされているためのメソッドをそのまま呼び出すことができます。

A4 Flavorソースの配置

作成したFlavorソースは、WEB-INF/src/下に置きます。配置されたFlavorソースは必要に応じてコンパイルされ実行されます。

例:

A5 アクセスパスとFlavorソースのマッピングルール

http://localhost:8080/webflavor/ にWeb Flavorを配置した場合、以下のようにマッピングされ実行されます。

URLパス Flavorソースパス
/webflavor/Foo.scala WEB-INF/src/Foo.scala
/webflavor/Foo WEB-INF/src/Foo.scala
/webflavor/ WEB-INF/src/Index.scala
/webflavor/Index WEB-INF/src/Index.scala
/webflavor/Index.scala WEB-INF/src/Index.scala
/webflavor/examples/Boo.scala WEB-INF/src/examples/Boo.scala
/webflavor/examples/Boo WEB-INF/src/examples/Boo.scala

アクセスパスが"/"で終わる場合は、".../Index.scala"の扱いになります。

A6 Web Flavor filter (web.xml) の設定

Web Flavor filterとは、Web Flavorを動かす為のランタイムで、ServletのFilterになっています。

デフォルトの設定(web.xml)は以下のようになっており、

<?xml version="1.0" encoding="ISO-8859-1"?>

<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_5.xsd"
    version="2.5">
...
  <filter>
    <filter-name>WebFlabor</filter-name>
    <filter-class>
      jp.ne.cappuccino.keisuken.servlet.flavor.WebFlavorFilter
    </filter-class>
    <init-param>
      <param-name>filterChain</param-name>
      <param-value>false</param-value>
    </init-param>
<!--
    <init-param>
      <param-name>source</param-name>
      <param-value>WEB-INF/src</param-value>
    </init-param>
    <init-param>
      <param-name>libSource</param-name>
      <param-value>WEB-INF/lib_src</param-value>
    </init-param>
    <init-param>
      <param-name>temporary</param-name>
      <param-value>WEB-INF/temp</param-value>
    </init-param>
    <init-param>
      <param-name>startupCompileAll</param-name>
      <param-value>true</param-value>
    </init-param>
    <init-param>
      <param-name>updateCheckInterval</param-name>
      <param-value>60</param-value>
    </init-param>
    <init-param>
      <param-name>staticMode</param-name>
      <param-value>false</param-value>
    </init-param>
    <init-param>
      <param-name>bbsHome</param-name>
      <param-value>/WEB-INF/bbs_data</param-value>
    </init-param>
    <init-param>
      <param-name>wikiHome</param-name>
      <param-value>/WEB-INF/wiki_data</param-value>
    </init-param>
-->
  </filter>

  <filter-mapping>
    <filter-name>WebFlabor</filter-name>
    <url-pattern>/</url-pattern>
  </filter-mapping>
  <filter-mapping>
    <filter-name>WebFlabor</filter-name>
    <url-pattern>/Index.scala</url-pattern>
  </filter-mapping>

  <filter-mapping>
    <filter-name>WebFlabor</filter-name>
    <url-pattern>*.scala</url-pattern>
  </filter-mapping>

  <filter-mapping>
    <filter-name>WebFlabor</filter-name>
    <url-pattern>/samples/*</url-pattern>
  </filter-mapping>

  <filter-mapping>
    <filter-name>WebFlabor</filter-name>
    <url-pattern>/admin/*</url-pattern>
  </filter-mapping>

"/"、"/Index.scala"、"*.scala"、"/samples/*"、"/admin/*"にそれぞれマッピングされています。

また、Web Flavor Filterは、以下のパラメータを持っています。

パラメータ名 内容
filterChain Filterをchainする場合に使います。デフォルトは"false"で"true"にするとchainされます
source Flavorソースを配置するディレクトリを示します。デフォルトは"WEB-INF/src"で、変更する場合はWARファイルを配置したディレクトリから相対パスで指定してください
libSource Flavorソースに依存する動的ライブラリを配置するディレクトリです。デフォルトでは"WEB-INF/lib_src"で、変更する場合はWARファイルを配置したディレクトリから相対パスで指定してください
temporary Flavorをコンパイルするためのテンポラリディレクトリを示します。デフォルトは"WEB-INF/temp"で、変更する場合はWARファイルを配置したディレクトリから相対パスで指定してください
startupCompileAll スタートアップ時にFlavorソースをすべてコンパイルするかどうかを示します。デフォルトはfalseで実行しません。trueにするとすべてコンパイルします。ただし起動に時間がかかる可能性があります
updateCheckInterval Flavorソースの更新チェック間隔(秒)を示します。デフォルトは60秒です
staticMode Flavorを静的モード(静的クラスからインスタンス化するかどうか)を示します。デフォルトはfalseです。trueにすると、デフォルトクラスローダからFlavorを生成して実行します。静的モードにすると、あらかじめコンパイルされているため起動時間が早くなります。ただし動的にコンパイルされなくなるため注意が必要です

A7 Administrator menu

Administrator menuは、Web Flavorの管理メニューです。

主な機能は、

です。

なお、セキュリティの設定(web.xml)は、以下のようになっており、"web-flavor-admin"ロールでBASIC認証されています。必要に応じて変更してください。

  <security-constraint>
    <web-resource-collection>
      <web-resource-name>Admin area</web-resource-name>
      <url-pattern>/admin/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
      <role-name>web-flavor-admin</role-name>
    </auth-constraint>
  </security-constraint>

  <login-config>
    <auth-method>BASIC</auth-method>
    <realm-name>Admin area</realm-name>
  </login-config>

A8 Flavor Compiler

A8.1 Flavor Compilerコマンド

$FLAVOR_HOME/bin に flavorc (UNIX/Linux用シェル) および flavorc.bat があるので、それらにPATHを通してください。

flavorcのArgumentsは以下の通りです。

Usage: flavorc CLASSPATH DESTDIR SRCDIR
CLASSPATH: コンパイルに必要なCLASSPATH
DESTDIR: コンパイルされたクラスが置かれるディレクトリ
SRCDIR: Flavorが置かれているディレクトリ

A8.2 Flavor Compiler Antタスク

flavorc Antタスクを使うためには、Antタスク内でカスタムタスクを定義する必要があります。

<taskdef name="flavorc"
  classname="jp.ne.cappuccino.keisuken.servlet.flavor.tools.FlavorCompilerTask"
  classpath="${scala-compiler-jar}:${scala-library-jar}:${webflavor-jar}"
/>

次にflavorc Antタスクを使ってコンパイルの定義を記述します。

<!--
${classpath}: コンパイルに必要なCLASSPATH
${destdir}: コンパイルされたクラスが置かれるディレクトリ
${srcdir}: Flavorが置かれているディレクトリ
-->
<flavorc
  srcdir="${flavor-srcdir}"
  destdir="${classes}"
  classpath="${classpath}"
/>

A9 POHPテンプレート

A9.1 POHPテンプレートとは

POHPテンプレートは、UTF-8で書かれたHTMLファイルです。

このテンプレートファイルにFlavorによって与えられるXMLリソースを埋め込んで最終的なページ(HTML/XHTML)を出力します。

A9.2 POHPテンプレート命名規則

Flavorソース名と同じディレクトリに同じ名前をつけて配置します。ただし拡張子は".html"をつけてください。

Flavorソースパス HTMLテンプレートパス
src/examples/Foo.scala src/examples/Foo.html

A9.3 POHPリソース

POHPテンプレートであるHTMLには、Flavorから挿入したい個所にリソースタグを記述します。

<!-- src/examples/Foo.html -->
<html>
<body>
<span flavor:id="foo">ダミーメッセージ</span>
</body>
</html>

リソースタグは、次の2つの形式があります。

リソース名は、Flavorで挿入する名前と同じものにしてください。

Flavorは、以下のように記述します。

// src/examples/Foo.scala
Template("ページ名",
  "foo" -> <h1>Hello, world!</h1>
)

Foo.htmlのリソースタグの場所に、リソース名で与えたXML要素(Seq[scala.xml.Node]を挿入してくれます。

またページ名は、同じURLのFlavor(Foo.html)に複数のページを返す為に必要なもので、デフォルト時は""(0文字)を与えます。

POHPテンプレートの命名規則は、以下のようになります。

Flavorパス ページ名 HTMLテンプレートパス
examples/Foo.scala "" examples/Foo.html
examples/Foo.scala "boo" examples/Foo_boo.html
exmaples/Foo.scala "bar" examples/Foo_bar.html

A10 動的ライブラリ

A10.1 動的ライブラリとは

動的ライブラリとはFlavorに依存した動的に決定されるライブラリであり、Flavorとともに動的にコンパイルされ、Flavorと同じClassLoader上で実行されます。

動的ライブラリを使うことでWebアプリケーションのコードを共有でき、Flavorの開発とともに試行錯誤しながら開発を進めることができます。

A10.2 動的ライブラリの配置

動的ライブラリは、通常"WEB-INF/lib_src"に置かれ、Flavorから参照されます。

A10.3 パッケージ

ルートパッケージでは参照されない為、必ずパッケージをつけます。

// WEB-INF/lib_src/examples/HelloWorld.scala
package examples

object HelloWorld {
  def message = "Hello, world!"
}

A11 履歴

A12 ToDo