结合使用 Apache Geronimo 和 Lift

来源:developerWorks 中国 作者:Michael Galpin
  
Lift 是一种新的 Web 应用程序框架。它是一种在 Scala 编程语言上构建的可伸缩框架。它非常适合 Apache Geronimo 这样的可伸缩应用服务器,尤其是在 Scala 编译为与 Java™ 语言非常相似的字节码并使用 Java 平台之后。在本文中,学习如何用 Lift 创建 Web 应用程序并把它部署到 Geronimo。

Lift Web 应用程序框架需要 Scala,而 Scala 依赖于 Java Development Kit,所以也需要安装 Java Development Kit。下面是编写本文时使用的软件。

Java Development Toolkit
Scala 需要 JDK V1.5 或更高版本。我使用 Java V1.5.0_13。也可以使用 IBM Java 2 Platform。
Scala
我使用 Scala V2.6.1。根据 Scala Web 站点上的说法,Scala 软件发行版最好安装在 Unix® 或 Windows® 系统上,它需要 Java 2 Runtime Environment(例如 Sun Microsystems 或 IBM® 提供的 JRE)的 V1.4 或更高版本。
Apache Maven
Lift 依靠 Apache Maven 设置项目、测试代码等。需要 Apache Maven V2.0.7 或更高版本,本文使用 V2.0.9。
Apache Geronimo
Lift 最好与 Jetty 一起使用,所以我使用带 Jetty 的 Apache Geronimo V2.1.1。
一个数据库
在默认情况下,Lift 使用嵌入的 Apache Derby 数据库,但是也可以使用 MySQL 或 Postgres。

Lift 是用 Scala 编写的,但是本文不要求您熟悉 Scala。需要熟悉 Java 语言和 Java Web 编程。熟悉 Maven 会有帮助,因为 Lift 在许多方面都使用 Maven。

为什么要使用 Lift

在讨论如何下载库和执行脚本之前,应该先谈谈为什么要使用 Lift。毕竟,现在有许多其他 Web 框架,其中一部分也可以在 Geronimo 中使用。本文将讨论重要的 Lift 结构,包括片段和模型,以此说明用 Lift 创建和测试 Web 应用程序是多么容易。

Java Web 程序员有许多可供选择的 Web 框架。一些人甚至认为现在的 Web 框架太多了,让人无所适从。那么,在众多的 Web 框架中,Lift 处于什么位置呢?Lift 是用 Scala 编写的,而不是 Java。尽管 Scala 编译为 Java 字节码,但是与在 JVM 上运行的许多动态语言不同,它并不是某种包装器代码。Scala 是一种静态类型语言,作为快速的 “原生” Java 运行。这应该不令人吃惊,因为 Scala 的创建者曾经编写过 Java 编译器。因此,Scala 是一种表达能力比 Java 强得多的语言,但是它在 JVM 上运行,运行速度与 Java 一样快。这些性质使它非常适合实现 Web 框架 —— 这种强大的语言使我们编写的代码更少,同时保持出色的性能。

Scala 是非常出色的,那么 Lift 呢?Lift 充分利用了 Scala 的优点。它可以在任何 Java Web 容器中运行,因为它使用标准的 Java servlet 和 servlet 过滤器。Lift 的片段和模型充分利用了灵活的 Scala 语法。通过使用 Scala 的基于 actor 的并发系统,Lift 还能够出色地执行 Comet 风格的 Ajax。您不相信吗?我们来看看用 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 替我们生成一个应用程序。这是应用程序框架中的一个常见特性,最初是由 Ruby on Rails 实现的。在这里,可以通过几个选项指定要创建的应用程序骨架的类型。每种应用程序类型对应于一个原型(archetype),groupId、artifactId 和版本惟一地标识了原型和要创建的应用程序骨架。remoteRepositories 参数向 Maven 指出要使用的 Maven 存储库。最后两个参数(groupId 和 artifactId)对于您的应用程序是惟一的。在这里,我们使用 org.developerworks.lift 和 quepasa 作为参数值,但是您应该在这里设置自己的值。

仔细看看清单 1,应该会看到几个粗体行。这实际上是 Maven 产生的用户提示。它要求输入应用程序的版本和应用程序的包名。输入之后,它会替您创建应用程序骨架。完成之后,应该会看到与图 1 相似的结构。


图 1. 应用程序结构
应用程序结构

这就是 Lift 应用程序的结构。/src/main 是放置所有 Scala 代码的地方。在这里可以找到 Boot 类,这个类定义应用程序的许多配置参数。在 Boot 类中还有 XML 配置文件中常见的一些内容。

/webapp 目录是放置 Web 工件的地方,比如 HTML、JavaScript 和 CSS。那么 JSP 或等效的东西呢?根据设计,在 Lift 中没有这样的东西。在 Lift 中,使用包含一些扩展标记的 XHTML 文件,扩展标记让 Scala 能够注入动态内容。采用片段的形式提供内容。

在 /webapp 目录中还应该注意两个东西。第一个是 WEB-INF 目录。这是 Java Web 开发中典型的 WEB-INF。部署 Lift 应用程序的方法是,把它打包为 WAR 文件并发送给 Java Web 容器(比如 Geronimo)。还要注意 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 标记。还记得前面提到的模板吗?这个标记就调用这个模板。lift:surround 让 Lift 使用一个称为 “surround” 的片段。这是 Lift 包含的一个标准片段。它与 JSP 中的定制标记相似,至少在语法方面是相似的。在这里,我们使用默认模板。这会引入 default.html 模板中定义的所有标准 HTML 内容,比如 HTML 头和体标记等等。可以修改默认模板或创建自己的模板,并通过 surround 模板引用它。

为了在应用程序中添加新页面,还需要做一项工作。前面提到 Boot 类,以及如何用它定义配置数据。它定义的配置之一是一个站点地图,所以需要在站点地图中添加新的 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 值中设置。使用 :: 表示法创建一个列表字面值。这里已经有一个 “Home” 条目,它映射到 “/”(index.html 页面)。Nil 表示列表的结尾。

我们已经创建了一个页面并把它添加到站点地图中,现在来测试这个应用程序。在使用 Lift 时,测试是非常容易的。

测试应用程序

测试 Lift 应用程序之前应该先对代码进行打包,然后启动数据库和应用服务器,对吗?别着急。与许多现代 Java Web 框架一样,Lift 使用嵌入的数据库(Derby)和嵌入的 Web 容器(Jetty)。因此,不需要打包或外部软件,也可以测试应用程序。我们只需使用 Maven,如下所示:


清单 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.

在第一次运行这个命令时,会看到 Maven 下载运行 Jetty 所需的几个 JAR 文件。完成之后,就可以通过访问 http://localhost:8080 启动 Lift 应用程序。


图 2. 欢迎页面
欢迎页面

如果单击 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 变量,它要么是空字符串,要么是提交的值加上 Received: 字符串前缀(如果表单提交了值)。bind 部分把数据绑定到视图中的每个标记。这里的数据是 XML。text 和 submit 函数是 XML 构建器。对于 messages 标记,我们直接使用 XML。XML 是 Scala 中的第一类成员,Lift 可以直接使用 XML。

为了测试新代码,只需进行重新编译,同样使用 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. ¿Qué pasa? V2.0
Que pasa? V2.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 目录。这是放置模型类的地方,其中已经有一个模型类: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 中直接声明为对象的任何东西都是一个单实例对象(singleton)。单实例对象 Message 作为 Message 类的元存储库和工厂类。注意,它扩展 KeyedMetaMapper 对象。在 Scala 中,方括号表示参数化的类;在这里,参数是 ID 的类型和实际模型类的类型。这种强大的构造使 Message 对象很容易使用。

Message 类只定义两个字段:id 和 text。这些字段映射到数据库。类型(比如 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>
    )
  }
}

这里添加了两个特性。首先,如果提交了新消息,就创建一个新的 Message 模型并把它保存到数据库中。接下来,从数据库装载所有 Message。我们使用 foldLeft 构造循环遍历 Message 集合(假设它是从左到右出现的);在每次迭代中,把当前表达式(最初是空字符串,传递给 foldLeft 的第一个参数)替换为闭包(传递给 foldLeft 的第二个参数)的计算结果。在这里,实际效果是把所有消息的文本连接在一起,消息之间以 ### 字符串分隔。不一定非以这种方式进行消息格式化,但是它演示了如何使用 Lift 的 OR 框架以及 Scala 的强大功能。

在重新编译应用程序之前,还需要做一件事情。在 Boot 类中,需要告诉应用程序数据库有一个新模型。这会在数据库中创建一个表。这只需要一行代码。


清单 12. 在 Boot 类中添加模型
                
Schemifier.schemify(true, Log.infoF _, Message)

在清单 3 所示的 Boot 类中,可以看到用来建立数据库连接的逻辑。Lift 可以通过 “约定优先于配置” 方式使用 JNDI 提供的连接:它会自动寻找与 JNDI 名称绑定的数据源。如果没有 JNDI 数据源,它会使用 DBVendor 对象管理连接。如果希望使用嵌入的 Derby 数据库之外的其他数据库,可以在这里添加连接信息。

我们已经了解了 Lift 的基本功能,下面看看如何把 Lift 应用程序部署到 Geronimo。





部署到 Geronimo

Scala 语法的表达能力比 Java 强得多。尽管如此,Scala 会编译为与 Java 非常相似的字节码。实际上,很难区分 Scala 的字节码和从 Java 源代码生成的字节码,其性能也与 Java 相当。Lift 依赖于 Scala,因此编程模型与典型的 Java Web 应用程序框架很不一样。但是,Lift 仍然作为 Java Web 应用程序运行。在使用嵌入的 Jetty 时,就会以 Java Web 应用程序的形式运行 Lift 应用程序。还可以非常轻松地用 Lift 创建 Java WAR,然后把 WAR 部署到任何 Web 容器中,包括 Geronimo。

创建 WAR

在创建 WAR 之前,需要设置 Geronimo。在 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,就可以把它部署到 Geronimo 了。

部署 WAR

把应用程序部署到 Geronimo 很容易,对于 Lift Web 应用程序也是如此。可以使用 Geronimo 控制台,见图 5。


图 5. Geronimo 控制台
Geronimo 控制台

使用 Archive > Browse 按钮导航到 Maven 在清单 14 中创建的 WAR 文件并安装这个应用程序。安装之后,可以访问 http://locahost:8080/quepasa,如下所示。


图 6. 在 Geronimo 上运行的 Lift
在 Geronimo 上运行的 Lift

这个应用程序的表现与测试期间完全一样。以这个应用程序为基础,可以进一步进行定制,以添加更多的页面和模型等等。可以通过修改主模板来定制它的外观。还可以创建供 Lift 使用的数据库池绑定,从而使与 Geronimo 的集成更紧密。





结束语

本文演示了如何设置和运行 Lift。我们看到了 Lift 的所有基本组成部分:引导类、视图、片段和模型。还看到如何按照以视图为中心的方式把这些组件组合成 Web 应用程序。我们看到了把 Lift 应用程序部署到 Geronimo 是多么容易,这使我们能够享受 Geronimo 提供的众多好处。我们还可以实现许多特性,而且 Lift 仍然非常年轻,有很大的发展余地。请随时关注 Lift 的发展并使用它满足您的需要。(责任编辑:A6)


时间:2008-11-18 09:41 来源:developerWorks 中国 作者:Michael Galpin 原文链接

好文,顶一下
(0)
0%
文章真差,踩一下
(0)
0%
------分隔线----------------------------


把开源带在你的身边-精美linux小纪念品
无觅相关文章插件,快速提升流量