Apache Geronimo に Lift を活用する

Lift で Web アプリケーションを作成し、Geronimo にデプロイする

Lift は Web アプリケーションの新しいフレームワークです。Lift は Scala プログラミング言語をベースに構築された非常にスケーラブルなフレームワークであるため、Apache Geronimo などの非常にスケーラブルなサーバーと組み合わせて使うには最適です。特にベースとなっている Scala が Java™ 言語と同様にバイトコードにコンパイルされ、Java プラットフォームを活用できることも、最適な組み合わせである大きな理由のひとつです。この記事では、Lift を使って Web アプリケーションを作成する方法、そしてそのアプリケーションを Geronimo にデプロイする方法を学びます。

Michael Galpin, Developer, eBay

Galpin Michael photoMichael Galpin は、1998年以来、プロとして Java ソフトウェアを開発してきています。彼は現在 eBay に勤務しています。彼は California Institute of Technology で数学の学位を取得しています。



2008年 7月 22日

Lift Web アプリケーション・フレームワークには Scala が必要です。また、Scala は JDK (Java Development Kit) に依存しているため、JDK も必要になります。この記事を書くにあたり、使用したソフトウェアは次のとおりです。

JDK
Scala には JDK V1.5 以上が必要です。ここでは Java V1.5.0_13 を使いました。また IBM Java 2 Platform を使うこともできます。
JDScala
ここでは Scala V2.6.1 を使いました。Scala の Web サイトによると、Scala ソフトウェアのディストリビューションをインストーする環境としては Unix® システムまたは Windows® システムが最適です。Scala には V1.4 以降の Java 2 Runtime Environment (つまり Sun Microsystems の JRE または IBM® の JRE) が必要です。
Apache Maven
Lift はプロジェクトのセットアップやコードのテストなどに Apache Maven を利用します。Apache Maven V2.0.7 以降が必要であり、この記事では V2.0.9 を使いました。
Apache Geronimo
Lift は Jetty と組み合わせた場合に最適な動作をします。そのため、ここでは Apache Geronimo V2.1.1 の Jetty 版を使用しました。
データベース
Lift はデフォルトで組み込みの Apache Derby データベースを使いますが、MySQL や Postgres を使うこともできます。

Lift は Scala で作成されてはいますが、この記事では Scala に関する知識はあまり必要としません。ただし Java 言語と Java による Web プログラミングについては十分理解している必要があります。また、Lift では Maven を多用するため、Maven についても理解していると役に立ちます。

なぜ Lift なのか

ライブラリーのダウンロードやスクリプトの実行の説明を始める前に、そもそもなぜ Lift を使う必要があるのかを説明する必要があるでしょう。ご存じのように、世の中には他にも数え切れないほどの Web アプリケーションのフレームワークがあり、それらもすべて Geronimo で適切に動作します。この記事では、スニペットやモデルなど Lift の重要な構成体を説明しながら、Lift を使うことで Web アプリケーションの作成やテストがどれほど容易に行えるかを説明します。

Java Web プログラマーは幸運にも、Web フレームワークに関しては非常に幅広い選択肢を与えられています。中には選択肢が多すぎると言う人さえいるかもしれません (選択肢が多すぎることなどあるのでしょうか)。そうした中で Lift はどんな位置にあるのでしょう。Lift は Scala で作成されており、Scala は Java ではありません。しかし Scala は Java バイトコードにコンパイルすることができます。このバイトコードは JVM 上で実行する多くの動的言語に見られるようなエミュレートされた一種のラッパー・コードではありません。Scala は静的型付け言語であり、高速な「ネイティブ」Java として実行されます。これは驚くにはあたりません。なぜなら Scala の作成者は Java コンパイラーの作成者でもあるからです。つまり Scala を使うことによって、Java よりもはるかに表現力豊かでありながら JVM 上で実行され、Java と同程度の速さで実行される言語を手にしたことになります。こうしたことは Web フレームワークの要素として好都合であり、Scala は作成すべきコードは少なく、しかも同時にパフォーマンスの高い強力な言語であるということです

このように Scala は素晴らしい言語ですが、では Lift はどうなのでしょう。Lift は Scala を最大限に活用しています。Lift は標準の Java サーブレットやサーブレット・フィルターを使用するため、任意の Java Web コンテナーで動作します。Lift のスニペットやモデルは、Scala の柔軟な構文を十分に活用しています。また Lift によって Comet スタイルの Ajax が非常にうまく動作しますが、これは Scala でのアクター・ベースの並行システムのおかげです。まだ信じられないでしょうか。では、Lift を使って Web アプリケーションを作成することがどれほど容易かを見てみましょう。


Lift アプリケーションを作成する

どのような Web アプリケーション・フレームワークであれ、最初に必要なことはそのフレームワークをダウンロードすることではないでしょうか。しかし Lift の場合にはそうではありません。Lift ではほとんどすべてのことに Maven を使い、最初のセットアップも例外ではありません。しかし Maven を使い始める前に、必ず SCALA_HOME 環境変数を設定しておきます。次に、下記のコマンドを実行します。

リスト 1. 新しい Lift アプリケーションを作成する
$ mvn archetype:generate -U -DarchetypeGroupId=net.liftweb 
-DarchetypeArtifactId=lift-archetype-basic -DarchetypeVersion=0.8 
-DremoteRepositories=http://scala-tools.org/repo-releases 
-DgroupId=org.developerworks.lift -DartifactId=quepasa
[INFO] Scanning for projects...
[INFO] Searching repository for plugin with prefix: 'archetype'.
[INFO] org.apache.maven.plugins: checking for updates from central
[INFO] org.codehaus.mojo: checking for updates from central
[INFO] artifact org.apache.maven.plugins:maven-archetype-plugin: checking for 
updates from central
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Default Project
[INFO]    task-segment: [archetype:generate] (aggregator-style)
[INFO] ------------------------------------------------------------------------
[INFO] Preparing archetype:generate
[INFO] No goals needed for project - skipping
[INFO] Setting property: classpath.resource.loader.class =>
'org.codehaus.plexus.velocity.ContextClassLoaderResourceLoader'.
[INFO] Setting property: velocimacro.messages.on => 'false'.
[INFO] Setting property: resource.loader => 'classpath'.
[INFO] Setting property: resource.manager.logwhenfound => 'false'.
[INFO] [archetype:generate]
[INFO] Generating project in Interactive mode
[INFO] Archetype repository missing. Using the one from 
  [net.liftweb:lift-archetype-basic:0.6 -> http://scala-tools.org/repo-releases]
  found in catalog internal
Define value for version:  1.0-SNAPSHOT: : 0.1
Confirm properties configuration:
groupId: org.developerworks.lift
artifactId: quepasa
version: 0.1
package: org.developerworks.lift
 Y: : y
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating OldArchetype: lift-archetype-basic:0.8
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: org.developerworks.lift
[INFO] Parameter: packageName, Value: org.developerworks.lift
[INFO] Parameter: basedir, Value: /Users/michael/code/lift
[INFO] Parameter: package, Value: org.developerworks.lift
[INFO] Parameter: version, Value: 0.1
[INFO] Parameter: artifactId, Value: quepasa
[INFO] ********************* End of debug info from resources from generated POM 
***********************
[INFO] OldArchetype created in dir: /Users/michael/code/lift/quepasa
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 8 seconds
[INFO] Finished at: Wed May 21 21:49:56 PDT 2008
[INFO] Final Memory: 8M/14M
[INFO] ------------------------------------------------------------------------

Maven に入力したコマンドによって、アプリケーションを生成するように Maven に命令します。これはアプリケーション・フレームワークの一般的な機能であり、Ruby on Rails で最初に使われてからよく使われるようになりました。この場合、アプリケーションのスケルトンとしてどのようなものを作成するかに関して、いくつかのオプションがあります。それぞれのオプションが、ある原形に対応し、groupIdartifactId、version によってその原形が一意に特定され、これらに従ってアプリケーションのスケルトンが作成されます。引数 remoteRepositories は Maven に対して、どんな Maven リポジトリーを使うのかを伝えます。最後の 2 つの引数 (groupIdartifactId) はアプリケーション固有のものです。ここではこれらの値として org.developerworks.liftquepasa を使いましたが、ここには皆さん独自の値を入れることができます。

リスト 1 のもっと下の方を見ると、いくつか太字の行があることに気付くはずです。これらは実は Maven のユーザー・プロンプトです。このプロンプトはアプリケーションのバージョンとアプリケーションのパッケージを入力するように要求します。それが終わると、Maven はアプリケーションのスケルトンの作成を完了します。すると図 1 のような構造が作成されているはずです。

図 1. アプリケーションの構造
アプリケーションの構造

これが Lift アプリケーションの構造です。/src/main にはすべての Scala コードが置かれます。このディレクトリーには、アプリケーションの構成パラメーターの多くを定義する Boot クラスがあります。XML の構成ファイルの中に通常見られるようなものの一部は、この Boot クラスの中にあります。

/webapp ディレクトリーには Web 成果物 (HTML や JavaScript、CSS など) が置かれます。JSP または JSP に相当するものはどうなのか、と聞きたくなるかもしれません。Lift には JSP や JSP に相当するものはなく、これもまた、設計上そうなっているのです。Lift では、Scala が動的コンテンツを注入できるような拡張マークアップを含んだ XHTML ファイルを使います。そうしたコンテンツはスニペットという形をとっています。

/webapp ディレクトリーには、注目に値するものがさらに 2 つあります。その 1 つは WEB-INF ディレクトリーです。これは Java による Web 開発でおなじみの、従来の WEB-INF です。Lift アプリケーションをデプロイするためには、WAR ファイルとしてパックし、それを Java Web コンテナー (Geronimo など) に送信します。注目に値するもう 1 つは、templates-hidden ディレクトリーです。ここにアプリケーションの「マスター」テンプレートを配置します。こうすることによって、ルック・アンド・フィールのグローバルな変更を容易に行うことができます。

これで基本的な Lift アプリケーションを作成できたので、このアプリケーションへの追加を始めることができます。Lift ではビュー中心の手法で Web 開発を行います。モデルとコントローラーを作成する代わりにビューをまず作成し、そこから出発します。では、アプリケーションにページを追加することから始めましょう。

ページを追加する

Lift ではビューを作成するためにスクリプトを実行する必要はありません。ビューは単なる HTML ファイルなので、/webapp ディレクトリーの中に新しい HTML ファイルを作成します。単純なフォームを次に示します。

リスト 2. 単純なフォーム (update.html)
<lift:surround with="default" at="content">
    <div class="heading" id="title">¿Qué pasa?</div>
    <form method="POST">
        <label for="update">What's going on? </label>
        <input type="text" name="update"/>
        <input type="submit" value="update"/>
    </form>
</lift:surround>

ここで唯一特別なのは lift:surround タグです。前のセクションで説明した、templates-hidden ディレクトリーに配置されたテンプレートを覚えているでしょうか。このテンプレートは次のように呼び出されます。lift:surround は surround というスニペットを使うように Lift に命令します。surround は Lift に含まれる標準のスニペットです。これは JSP に見られるカスタム・タグに (少なくとも構文に関しては) 似ています。ここではデフォルトのテンプレートを使っており、これによって default.html テンプレートの中で定義される標準的なすべての HTML 要素 (HTML の head タグや body タグなど) が取り込まれます。このデフォルトのテンプレートは変更することができます。あるいは独自のテンプレートを作成し、それを surround テンプレートによって参照することもできます。

アプリケーションに新しいページを追加するために、もう 1 つしなければならないことがあります。先ほど Boot クラスに触れ、このクラスを使って構成データを定義する方法を説明しました。Boot クラスによって定義される構成の 1 つがサイト・マップです。つまりこの新しい HTML ページをサイト・マップに追加する必要があります (リスト 3)。

リスト 3. Boot クラスのサイト・マップにページを追加する
package bootstrap.liftweb

import net.liftweb.util._
import net.liftweb.http._
import net.liftweb.sitemap._
import net.liftweb.sitemap.Loc._
import Helpers._
import net.liftweb.mapper.{DB, ConnectionManager, Schemifier, 
   DefaultConnectionIdentifier, ConnectionIdentifier}
import java.sql.{Connection, DriverManager}
import org.developerworks.lift.model._
 
/**
  * A class that's instantiated early and run.  It allows the application
  * to modify lift's environment
  */
class Boot {
  def boot {
    if (!DB.jndiJdbcConnAvailable_?) DB.defineConnectionManager
(DefaultConnectionIdentifier, DBVendor)
    // where to search snippet
    LiftRules.addToPackages("org.developerworks.lift")     
    Schemifier.schemify(true, Log.infoF _, User)

    LiftRules.addTemplateBefore(User.templates)

    // Build SiteMap
    val entries = Menu(Loc("Home", "/", "Home")) ::
         Menu(Loc("update", "/update", "The Update Page")) ::
        Nil 
    LiftRules.setSiteMap(SiteMap(entries:_*))
    S.addAround(User.requestLoans)
  }
}


object DBVendor extends ConnectionManager {
  def newConnection(name: ConnectionIdentifier): Can[Connection] = {
    try {
      Class.forName("org.apache.derby.jdbc.EmbeddedDriver")
      val dm = DriverManager.getConnection("jdbc:derby:quepasa;create=true")
      Full(dm)
    } catch {
      case e : Exception => e.printStackTrace; Empty
    }
  }
  def releaseConnection(conn: Connection) {conn.close}
}

サイト・マップは entries という値の中にあります。:: 表記を使ってリスト・リテラルを作成します。ご覧のとおり、「/」(index.html ページ) にマッピングされる「Home」エントリーが既にあります。Nil はリストの最後を表します。

これでページの追加が終わり、そのページをサイト・マップに追加できたので、アプリケーションのテストを行う準備が整いました。Lift を使うとテストも簡単に行うことができます。

アプリケーションをテストする

Lift アプリケーションをテストする時が来ました。通常であれば、コードをパッケージ化し、データベースとアプリケーション・サーバーを起動するところですが、そんなに慌てる必要はありません。最近の多くの Java Web アプリケーションと同様、Lift は組み込みのデータベース (Derby) と組み込みの Web コンテナー (Jetty) を活用します。そのため、パッケージ化したり、外部依存関係を設定したりせず、アプリケーションをテストすることができます。必要なことは、Maven を使うことだけです (リスト 4)。

リスト 4. Maven を使って Lift を実行する
$ mvn jetty:run -U
[INFO] Scanning for projects...
[INFO] Searching repository for plugin with prefix: 'jetty'.
[INFO] org.apache.maven.plugins: checking for updates from scala-tools.org
[INFO] org.apache.maven.plugins: checking for updates from central
[INFO] org.codehaus.mojo: checking for updates from scala-tools.org
[INFO] org.codehaus.mojo: checking for updates from central
[INFO] artifact org.scala-tools:maven-scala-plugin: checking for updates 
from scala-tools.org
[INFO] artifact org.scala-tools:maven-scala-plugin: checking for updates from central
[INFO] artifact org.mortbay.jetty:maven-jetty-plugin: checking for updates 
from scala-tools.org
[INFO] artifact org.mortbay.jetty:maven-jetty-plugin: checking for updates from central
[INFO] artifact net.sf.alchim:yuicompressor-maven-plugin: checking for 
updates from scala-tools.org
[INFO] artifact net.sf.alchim:yuicompressor-maven-plugin: checking for updates 
from central
[INFO] artifact org.apache.maven.plugins:maven-eclipse-plugin: checking for 
updates from scala-tools.org
[INFO] artifact org.apache.maven.plugins:maven-eclipse-plugin: checking for 
updates from central
[INFO] ------------------------------------------------------------------------
[INFO] Building quepasa
[INFO]    task-segment: [jetty:run]
[INFO] ------------------------------------------------------------------------
[INFO] Preparing jetty:run
[INFO] [resources:resources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [yuicompressor:compress {execution: default}]
[INFO] nb warnings: 0, nb errors: 0
[INFO] artifact org.mortbay.jetty:jetty: checking for updates from scala-tools.org
[INFO] artifact org.mortbay.jetty:jetty: checking for updates from central
[INFO] [compiler:compile]
[INFO] Nothing to compile - all classes are up to date
[INFO] [scala:compile {execution: default}]
[INFO] Compiling 2 source files to /Users/michael/code/lift/quepasa/target/classes
[INFO] [resources:testResources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [compiler:testCompile]
[INFO] Nothing to compile - all classes are up to date
[INFO] [scala:testCompile {execution: default}]
[INFO] Compiling 3 source files to /Users/michael/code/lift/quepasa/target/test-classes
 [INFO] [jetty:run]
[INFO] Configuring Jetty for project: quepasa
[INFO] Webapp source directory = /Users/michael/code/lift/quepasa/src/main/webapp
[INFO] web.xml file = /Users/michael/code/lift/quepasa/src/main/webapp/WEB-INF/web.xml
[INFO] Classes = /Users/michael/code/lift/quepasa/target/classes
2008-05-23 21:19:31.149::INFO:  Logging to STDERR via org.mortbay.log.StdErrLog
[INFO] Context path = /
[INFO] Tmp directory =  determined at runtime
[INFO] Web defaults = org/mortbay/jetty/webapp/webdefault.xml
[INFO] Web overrides =  none
[INFO] Webapp directory = /Users/michael/code/lift/quepasa/src/main/webapp
[INFO] Starting jetty 6.1.10 ...
2008-05-23 21:19:31.245::INFO:  jetty-6.1.10
2008-05-23 21:19:31.409::INFO:  No Transaction manager found - if your webapp 
requires one, please configure one.
2008-05-23 21:19:31.989::INFO:  Started SelectChannelConnector@0.0.0.0:8080
[INFO] Started Jetty Server
[INFO] Starting scanner at interval of 5 seconds.

初めてこのコマンドを実行すると、Jetty を実行するために必要ないくつかの JAR を Maven がダウンロードすることがわかります。そのダウンロードが終わると、http://localhost:8080 と入力すれば Lift アプリケーションを起動できるはずです。

図 2. Welcome ページ
Welcome ページ

このページで The Update Page をクリックすると、先ほど作成したページが表示されるはずです。

図 3. 更新されたページ
更新されたページ

ページのタイトルとリンクがページの一番下にあることに気付いた人がいるかもしれません。これらはすべて、次に示すデフォルトのテンプレートから得られたものです。

リスト 5. デフォルトのテンプレート
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:lift="http://liftweb.net/">
  <head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
    <meta name="description" content="" />
    <meta name="keywords" content="" />
    
    <title>Lift Web Framework</title>
    <lift:CSS.blueprint />
    <lift:CSS.fancyType />
    <script id="jquery" src="/classpath/jquery.js" type="text/javascript"/>
    <script id="json" src="/classpath/json.js" type="text/javascript"/>
    <style>
  /* <![CDATA[ */
.sidebar ul {
    margin:0;
    padding:0;
    border-bottom:1px solid #ccc;
}
     

.sidebar ul li {
    margin:0;
    padding:0;
    list-style:none;
    border:1px solid #ccc;
    border-bottom:none;
}

.sidebar ul li a {
    display:block;
    padding:3px;
    text-indent:30px;
    text-decoration:none;
}

.sidebar ul li a:hover {
    background-color: #eee;
}


  /* ]]> */
  </style>
  </head>
  <body>
    <div class="container">
      <div class="column span-12 last" style="text-align: right">
        <h1 class="alt">quepasa</h1>
      </div>

      <hr/>

      <div class="column span-6 colborder sidebar">
        <hr class="space" />
        <lift:Menu.builder />    
        <div>
          <lift:snippet type="msgs"/>
          <hr class="space" />
        </div>
      </div>

      <div class="column span-17 last">
        <lift:bind name="content" />
      </div>

      <hr />
      <div class="column span-23 last" style="text-align: center">
        <h4 class="alt">
          <a href="http://liftweb.net">
            <i>lift</i>
          </a> is Copyright 2007 WorldWide Conferencing, LLC.  
		  Distributed under an Apache 2.0 License.</h4>
      </div>

    </div>
  </body>
</html>

このタイトルは、このアプリケーションを作成するために使用した Maven スクリプトによって生成されたものです。タイトルはテンプレートの中で変更することができ、その変更がすべてのページに反映されます。lift:Menu.builder も Lift のスニペットです。この場合は Boot クラスのサイト・マップを使ってサイト・メニューを作成しています。これを見るとわかるように、スニペットは非常に強力です。スニペットは Lift アプリケーションを動的なものにするための鍵なのです。ここまでは Lift に含まれている標準のスニペットをいくつか使いましたが、今度はカスタムのスニペットを作成する方法を調べてみましょう。

スニペットを使用する

先ほど触れたように、Lift はビュー中心です。サーバー・サイドのコードは、Lift がビュー・コードの中にタグを見つけると実行されます。それを念頭に置いた上で見て欲しいのですが、リスト 6 はビュー・コードを更新したものです。

リスト 6. 更新されたビュー
<lift:surround with="default" at="content">
    <div class="heading" id="title">¿Qué pasa?</div>
    <lift:Update.show form="POST">
        <label for="update">What's going on? </label>
        <qp:update/>
        <qp:submit/>
        <qp:messages/>
    </lift:Update.show>
</lift:surround>

フォームを持つ代わりに、Lift のスニペットを使います。lift:Update.show は Lift に対して、Update というクラスの show というメソッドを呼び出すように命令します。qp:update タグは、このメソッドの中にバインドされている変数を出力します。

リスト 7. Update スニペット
package org.developerworks.lift.snippet

import scala.xml.NodeSeq
import net.liftweb.http.S._
import net.liftweb.http.SHtml._
import net.liftweb.http.RequestVar
import net.liftweb.util.Helpers._
import net.liftweb.util.Full

class Update {
  object qpx extends RequestVar(Full("")) // default is empty string
  
  def show(xhtml: NodeSeq): NodeSeq = {
    val temp = if (qpx.isEmpty || qpx.open_!.length == 0) "" else "Received: " 
+ qpx.open_!
    bind("qp", xhtml,
        "update" --> text("", v => qpx(Full(v))) % ("size" -> "10") % 
("id" -> "update"),
        "submit" --> submit(?("Update"), ignore => {}),
        "messages" --> <div>{temp}</div>
    )
  }
}

qpx オブジェクトはリクエスト変数です。show メソッドはフォームの中で送信された値にバインドされています。このスニペットでは temp 変数を作成していますが、temp 変数は空のストリングであるか、あるいはフォームに対して送信された値がある場合には、送信された値の前にReceived: というストリングを接頭辞として付加した値です。bind セクションはビューの中にあった各タグにデータをバインドします。この場合のデータは XML です。text 関数と submit 関数は XML ビルダーです。messages タグに関しては XML を直接使います。Scala では XML は第一級市民であり (ファーストクラス・オブジェクトとして扱うことができ)、Lift はそれを最大限活用しています。

新しいコードをテストするために必要なことは、再度コンパイルすることだけです。この場合も Maven を使います。

リスト 8. アプリケーションを再コンパイルする
$ mvn compile
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Building quepasa
[INFO]    task-segment: [compile]
[INFO] ------------------------------------------------------------------------
[INFO] [resources:resources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [yuicompressor:compress {execution: default}]
[INFO] nb warnings: 0, nb errors: 0
[INFO] [compiler:compile]
[INFO] Nothing to compile - all classes are up to date
[INFO] [scala:compile {execution: default}]
[INFO] Compiling 1 source files to /Users/michael/code/lift/quepasa/target/classes
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 7 seconds
[INFO] Finished at: Fri May 23 22:21:19 PDT 2008
[INFO] Final Memory: 9M/18M
[INFO] ------------------------------------------------------------------------

再コンパイルした理由は、新しい Scala クラスを追加したためです。もし HTML のビューを変更しただけであったら、再コンパイルは必要ありませんでした。サーバーの停止と再起動は必要なく、実行させたまま再コンパイルを実行することができます。するとアプリケーションは図 4 のようになります。

図 4. Update ページのバージョン 2.0
Update ページのバージョン 2.0

このページの HTML のソース・コードをちょっと見てください。その中にリスト 9 のようなものがあるはずです (これとまったく同じではないかもしれません)。

リスト 9. ソースの抜粋を見る
<div id="title" class="heading">¿Qué pasa?</div>
<form method="POST" action="/update">
    <label for="update">What's going on? </label>
    <input size="20" name="F1211607483014814000_QS4" type="text" value="" id="update" />
    <input name="F1211607483015168000_TRV" type="submit" value="Update" />
    <div>Received: Writing about Lift!</div>
</form>

フォーム・フィールドの名前が暗号のようになっていることに注目してください。こうすることでフォームの送信を装うことがずっと困難になるため、フォームのセキュリティーが高められています。こうしたことを、すべて Lift が行ってくれるのです。これでセキュアなフォームができたので、このフォームから送信されるデータを使って、もう少し興味深いことをしましょう。そのためには、このアプリケーションにモデルを追加する必要があります。

モデルを追加する

先ほどの図 1 で、model ディレクトリーがあることに気付いた方もいるのではないでしょうか。Lift ではこのディレクトリーにモデル・クラスを配置しますが、このディレクトリーには既に User というモデルがあります。これは Web ユーザー用の汎用のオブジェクト・モデルであり、そのまま使うこともできれば変更することもでき、あるいは破棄することもできます。このモデルに注目する理由は、このモデルが Lift の OR マッピングの多くの概念を示しているからです。今度は更新を保存するための新しいモデルを作成します。

リスト 10. Message モデル
package org.developerworks.lift.model

import net.liftweb.mapper._
import net.liftweb.util._

object Message extends Message with KeyedMetaMapper[Long, Message] {
    override def dbTableName = "message"
    override def fieldOrder = id :: text :: Nil
}

class Message extends KeyedMapper[Long, Message] {
  def getSingleton = Message // what's the "meta" object
  def primaryKeyField = id

  object id extends MappedLongIndex(this)
  object text extends MappedString(this, 1400)
}

Message クラスと Message オブジェクトがあることに注目してください。Scala では、オブジェクトとして直接宣言されるものはすべてシングルトンです。Message シングルトンには、Message クラスのメタデータのリポジトリーとして、またファクトリー・クラスとしての役割があります。Message シングルトンが KeyedMetaMapper オブジェクトを継承していることに注目してください。Scala では大括弧はパラメーター化されたクラスを表し、この場合、パラメーター化されたクラスは ID の型と実際のモデル・クラスの型を取ります。これは強力な構成体であり、これによって Message オブジェクトが使いやすくなっています。

Message クラスは単純に id と text という 2 つのフィールドを定義します。これらのフィールドはデータベースにマッピングされます。MappedString などの型は、フィールドとデータベースの型とのマッピング方法を Lift に伝えるだけではなく、そのフィールドの HTML の描画にも使われます。これで Message モデルをスニペットの中で使えるようになりました。

リスト 11. モデルを持つスニペット
package org.developerworks.lift.snippet

import scala.xml.NodeSeq
import net.liftweb.http.S._
import net.liftweb.http.SHtml._
import net.liftweb.http.RequestVar
import net.liftweb.util.Helpers._
import net.liftweb.util.Full
import org.developerworks.lift.model.Message

class Update {
  object qpx extends RequestVar(Full("")) // default is empty string
  
  def show(xhtml: NodeSeq): NodeSeq = {
    val msgSent = !(qpx.isEmpty || qpx.open_!.length == 0)
    if (msgSent){
        val msg:Message = Message.create
        msg.text(qpx.open_!).save
    }
    val messages = Message.findAll
    val temp = messages.foldLeft(""){(str, msg) => str + " ### " + msg.text}
    bind("qp", xhtml,
        "update" --> text("", v => qpx(Full(v))) % ("size" -> "10") % ("id" -> "update"),
        "submit" --> submit(?("Update"), ignore => {}),
        "messages" --> <div>{temp}</div>
    )
  }
}

スニペットには 2 つの内容が追加されています。まず、新しいメッセージが送られている場合には新しい Message モデルを作成し、それをデータベースに保存するようになりました。さらに、すべての Message をデータベースからロードします。このロードの処理は foldLeft 構成体によって Message (左から右に進むと考えてください) のコレクションに対して繰り返し行うことができ、繰り返しごとに、現在の式 (foldLeft に渡される第 1 のパラメーターで、最初は空のストリングです) をクロージャー (foldLeft に渡される第 2 のパラメーター) の評価結果で置き換えます。この場合はこれによって、実質的にすべてのメッセージのテキストが ### というストリングで区切られて結合されます。これはメッセージをフォーマット設定する方法としては、手の込んだ方法というわけではありませんが、Lift の OR フレームワークの使い方と Scala の強力さを示しています。

アプリケーションを再コンパイルする前に、もう 1 つしなければならないことがあります。Boot クラスの中で、データベースに新しいモデルがあることをアプリケーションに伝える必要があります。そうすることでデータベースの中にテーブルが作成されます。このために必要なコードはたった 1 行です。

リスト 12. Boot クラスにモデルを追加する
Schemifier.schemify(true, Log.infoF _, Message)

リスト 3 に示した Boot クラスの中に、このクラスがデータベース接続をするために使用するロジックがあることがわかります。Lift は JNDI によって提供される接続を使用しますが、この場合に Convention over Configuration (設定より規約) の手法を使い、JNDI 名にバインドされたデータ・ソースを自動的に探します。JNDI データ・ソースがない場合には、DBVendor オブジェクトを使って接続を処理します。組み込みの Derby データベース以外のデータベースを使いたい場合には、その接続情報をここに追加します。

これで Lift の基本的な機能が理解できたので、Lift をどのように Geronimo にデプロイするのかを調べてみましょう。


Geronimo にデプロイする

Scala には、Java よりもはるかに表現力の豊かな強力な構文があります。それにもかかわらず、Scala は Java とまったく同じようにバイトコードにコンパイルすることができます。実際、Scala のバイトコードは Java のソース・コードから作成されたバイトコードとほとんど区別できず、また Scala のパフォーマンスは Java と同等です。Lift は Scala を大いに活用することによって、典型的な Java Web アプリケーション・フレームワークとは大幅に異なる仕組みを実現することができます。とは言え、Lift は Java Web アプリケーションとして実行されます。組み込みの Jetty を使う際には、まさにそれが行われます。また、Java の WAR も Lift で非常に容易に作成することができ、この WAR は Geronimo を含む任意の Web コンテナーにデプロイすることができます。

WAR を作成する

WAR を作成する際には、Geronimo で使う上で適切な WAR を作成する必要があります。webapp/WEB-INF ディレクトリーの中で Geronimo のデプロイメント・プランを作成します。

リスト 13. Geronimo のデプロイメント・プラン
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://geronimo.apache.org/xml/ns/j2ee/web-1.1">
    <environment xmlns="http://geronimo.apache.org/xml/ns/deployment-1.1">
        <moduleId>
            <groupId>liftApps</groupId>
            <artifactId>quepasa</artifactId>
            <version>0.1</version>
            <type>war</type>
        </moduleId>
    </environment>
    <context-root>/quepasa</context-root>
</web-app>

/quepasa パスに注目してください。アプリケーションが Geronimo にデプロイされたら、このパスを使ってアプリケーションにアクセスします。今度は Maven を使って WAR ファイルを作成します。

リスト 14. Maven を使って WAR を作成する
$ mvn package
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Building quepasa
[INFO]    task-segment: [package]
[INFO] ------------------------------------------------------------------------
[INFO] [resources:resources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [yuicompressor:compress {execution: default}]
[INFO] nb warnings: 0, nb errors: 0
[INFO] [compiler:compile]
[INFO] Nothing to compile - all classes are up to date
[INFO] [scala:compile {execution: default}]
[INFO] Nothing to compile - all classes are up to date
[INFO] [resources:testResources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [compiler:testCompile]
[INFO] Nothing to compile - all classes are up to date
[INFO] [scala:testCompile {execution: default}]
[INFO] Nothing to compile - all classes are up to date
[INFO] Surefire report directory: /Users/michael/code/lift/
quepasa/target/surefire-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running org.developerworks.lift.AppTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.039 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO] [war:war]
[INFO] Packaging webapp
[INFO] Assembling webapp[quepasa] in [/Users/michael/code/lift/
quepasa/target/quepasa-0.1]
[INFO] Processing war project
[INFO] Webapp assembled in[811 msecs]
[INFO] Building war: /Users/michael/code/lift/quepasa/target/quepasa-0.1.war
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 31 seconds
[INFO] Finished at: Sun May 25 23:02:08 PDT 2008
[INFO] Final Memory: 13M/23M
[INFO] ------------------------------------------------------------------------

このコマンドを初めて実行すると、いくつかの JAR がダウンロードされます。これで WAR が作成されたので、この WAR を Geronimo にデプロイする準備が整いました。

WAR をデプロイする

アプリケーションを Geronimo にデプロイする作業は簡単です。これは Lift による Web アプリケーションの場合も同じです。デプロイするためには Geronimo のコンソールを使います (図 5)。

図 5. Geronimo のコンソール
Geronimo のコンソール

リスト 14 で Maven が作成した WAR ファイルまで Archive > Browse ボタンを使ってナビゲートし、アプリケーションをインストールします。インストールできると http://locahost:8080/quepasa を表示することができるはずです (図 6)。

図 6. Geronimo 上で実行する Lift
Geronimo 上で実行する Lift

すべてが、テストした場合とまったく同じように動作します。ここから、アプリケーションをさらにカスタマイズしたり、ページやモデルなどをさらに追加したりすることができます。マスター・テンプレートを変更して、アプリケーションのルック・アンド・フィールをカスタマイズすることもできます。また、Lift で使用できるようにバインドしたデータベース・プールを作成し、Geronimo ともっと深く統合することもできます。


まとめ

この記事では Lift を使ってアプリケーションを作成し、実行する方法を説明しました。そしてブートストラップ・クラスやビュー、スニペット、モデルなど、Lift の必須部分のすべてを調べました。またそれらを、ビュー優先で Web アプリケーションを作成する Lift の手法と組み合わせて使うための方法についても調べ、Lift アプリケーションを Geronimo にデプロイするのがいかに容易であるかを説明しました。Geronimo にデプロイすることで、Geronimo よる多くのメリットを活用することができます。Lift を使って既に多くのことができますが、Lift はまだ新しいフレームワークです。Lift が成熟していくのを注目し、なるべく Lift を活用してください。

参考文献

学ぶために

製品や技術を入手するために

  • Scala は一般的なプログラミング・パターンを簡潔でスマートに、そしてタイプセーフに表現できるように設計された汎用のプログラミング言語です。
  • Java 技術はプログラミング言語でありプラットフォームです。Sun Microsystems または IBM から JDK (Java Development Kit) をダウンロードしてください。
  • Apache のプロジェクトである Maven は、ソフトウェア・プロジェクトを管理し、理解するためのツールです。
  • Apache のプロジェクトである Geronimo は、オープンソースの手段を組み合わせ、開発者やシステム管理者の要求を満足するランタイムを作成するためのサーバー・ランタイム・フレームワークです。
  • Apache Derby は Apache DB のサブプロジェクトであり、Java で実装されたオープンソースのリレーショナル・データベースです。
  • 皆さんの次期オープンソース開発プロジェクトを IBM trial software を使って革新してください。ダウンロード、あるいは DVD で入手することができます。
  • IBM 製品の試用版をダウンロードし、DB2® や Lotus®、Rational®、Tivoli®、WebSphere® などが提供するアプリケーション開発ツールやミドルウェア製品をお試しください。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Open source
ArticleID=332856
ArticleTitle=Apache Geronimo に Lift を活用する
publish-date=07222008