 | Level: Intermediate Michael Galpin (mike.sr@gmail.com), Developer, eBay
22 Jul 2008 Lift is a new Web application framework. It is a highly scalable framework built
on the Scala programming language. It is the perfect partner for a highly scalable
application server, such as Apache Geronimo, especially since Scala compiles to byte
code just like the Java™ language and leverages the Java platform. In this article, you
will learn how to create a Web application using Lift and deploy it to
Geronimo.
The Lift Web application framework requires Scala, and Scala
depends on a Java Development Kit, so you need that, too. Here is the software used in
writing this article.
-
Java Development Toolkit
- You need JDK V1.5 or higher for Scala. We used Java V1.5.0_13. You can also use
the IBM Java 2 Platform.
-
Scala
- We used Scala V2.6.1
According to the Scala Web site, the Scala software distribution is best installed on a
Unix® or Windows® system. It requires V1.4 or later of the Java 2
Runtime Environment (e.g., JRE from Sun Microsystems or JRE from IBM®).
-
Apache Maven
- Lift relies on Apache Maven for setting up projects, testing code, etc. You need Apache Maven V2.0.7 or later, and in this
article, we used V2.0.9.
-
Apache Geronimo
- Lift works best with Jetty, so we used Apache Geronimo V2.1.1 with Jetty.
-
A database
- Lift uses an embedded Apache Derby database
by default, but you can also use MySQL or Postgres.
Lift is written in Scala, but we won't assume too much knowledge of Scala. Familiarity
with the Java language and Java Web programming is needed. Finally, familiarity
with Maven will also be useful, as Lift makes heavy use of it.
Why Lift
Before we start talking about downloading libraries and executing scripts, we should
talk about why you would want to use Lift in the first place. After
all, there are countless other Web application frameworks out there that will also work
just fine with Geronimo. This article will show how easy it is to create and test a Web
application using Lift by covering important Lift constructs, including snippets and models.
Java Web programmers are blessed with a plethora of choices when it comes to Web
frameworks. Some might even say there are too many choices, if there can be such a
thing. Where does Lift fit in? Lift is written in Scala, which is not Java. However,
Scala compiles to Java byte code, but this is not some kind of emulated wrapper code,
like you see with many dynamic languages that run on the JVM. Scala is a statically
typed language and runs as fast "native" Java. This should be no surprise because
Scala's creator also wrote the Java compiler. So with Scala, you get a much more
expressive language than Java, but it runs on the JVM and runs as fast as Java. These
are good ingredients for a Web framework — a powerful language that lets you write less
code and is high on the performance scale at the same time.
So Scala is great, but what about Lift? Lift takes great advantage of Scala. It works
in any Java Web container, as it makes use of a standard Java servlet and servlet
filter. Lift's snippets and models heavily exploit Scala's flexible syntax. Lift also
makes Comet-style Ajax perform especially well thanks to Scala's actor-based
concurrency system. Not convinced yet? Let's take a look at how easy it is to create a Web application using Lift.
Creating a Lift application
The first thing any Web application framework requires is a download, right? Not with
Lift. Lift uses Maven for just about everything, and the initial setup is no different.
Before we start using Maven, however, make sure you set the SCALA_HOME environment variable. Then issue the command shown below.
Listing 1. Create a new Lift application
$ 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] ------------------------------------------------------------------------
|
The command we fed into Maven tells it to generate an application for us. This is a
common feature in application frameworks, first popularized by Ruby on Rails. In this
case, we have a few options for what kind of application skeleton to create. Each
corresponds to an archetype, and groupId, artifactId, and version uniquely identify the
archetype and, thus, the application skeleton to create. The remoteRepositories argument tells Maven what Maven repository to
use. The last couple of arguments —
groupId and artifactId
— are unique to your application. In this case, we used
org.developerworks.lift and quepasa for the values, but this is where you would put in your own values.
Further down in Listing 1, you should see a few bold lines. These are actually user
prompts from Maven. It will ask you for the version of your application and the package
of your application. After that, it will finish creating the application skeleton
for you. When it is done, you should have a structure that looks similar to Figure 1.
Figure 1. Application structure
This is the structure of a Lift application. The /src/main is where all your Scala code
will go. This is where you find the Boot class, which
defines many of the configuration parameters of our application. Some of the things
you might normally find in an XML configuration file are, instead, found in the Boot class.
The /webapp directory is where we will put Web artifacts, such as HTML, JavaScript, and
CSS. What about JSPs or their equivalent, you ask? There is none of that in Lift, and,
again, this is by design. In Lift, you use XHTML files that included some extended
markup that allow for Scala to inject dynamic content. That content comes in the form
of snippets.
There are two more things you should take notice of in the /webapp
directory. First is the WEB-INF directory. This is the classic WEB-INF you are familiar
with from Java Web development. A Lift application is deployed by packing it up as a
WAR file and sending it to a Java Web container — like Geronimo. Finally,
notice the templates-hidden directory. This is where you put "master" templates for
your application. This allows you to easily make global changes to look and feel.
Now that we have created a basic Lift application, we are ready to start adding to it.
Lift takes a view-centric approach to Web development. Instead of creating models and
controllers, we create views first and go from there. So let's start by adding a page to our application.
Adding a page
You don't need to run any scripts to create a view in Lift. Your views are just HTML
files, so create a new HTML file in the /webapp directory. A simple form is shown below.
Listing 2. Simple form (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>
|
The only thing special here is the lift:surround tag. Remember the hidden-templates
described in the previous section? This how it gets invoked. The lift:surround tells Lift to use a snippet called "surround." This is
a standard snippet included with Lift. It is similar to a custom tag you might find in
JSPs, at least in terms of its syntax. In this case, we are using the default template.
That will pull in all the standard HTML stuff defined in the default.html template, like
the HTML head and body tags, etc. You can change the default template or create your own
and reference it through the surround template.
There is one more thing we need to do to add our new page to the
application. We mentioned the Boot
class and how it's used to define configuration data. One of the
configurations it defines is a site map, so we need to add the
new HTML page to the site map, as shown in Listing 3.
Listing 3. Adding a page to the Boot class' site map
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}
}
|
The site map is found in the entries value. The :: notation is used to create a list literal. As you can see, there is
already a "Home" entry that maps to "/" (the index.html page). Nil denotes the end of the list.
Now that we have added a page and added it to our site map, we are ready to test the
application. Lift makes this easy.
Testing the application
Time to test our Lift application, so package up the code and fire up the database and the
application server, right? Not so fast. Like many modern Java Web frameworks, Lift
takes advantage of an embedded database (Derby) and an embedded Web container (Jetty).
This lets us test our applications without any packaging or external dependencies. All
we need to do is use Maven, as shown below.
Listing 4. Running Lift with Maven
$ 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.
|
The first time you run this command, you will see Maven download several JARs it needs
to run Jetty. Once this is done, you should be able to bring up your Lift application by
going to http://localhost:8080.
Figure 2. Welcome page
If you click on Update Page, you should see the page we created earlier.
Figure 3. The update page
You might have noticed the title of the page and the links at the bottom. These all
come from the default template shown below.
Listing 5. The default template
<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>
|
The Maven script we used to create the application generated the title. You can change
it in the template, and it will be picked up on all pages. The lift:Menu.builder is another Lift snippet. In this case, it is using
the site map from the Boot class to create site menu. As
you can see, snippets are quite powerful. They are the key to making Lift applications
dynamic. We have used some standard snippets included with Lift, but let's take a look at creating a custom snippet.
Using a snippet
As mentioned, Lift is view-centric. Server-side code is executed when Lift encounters
tags in the view code. With that in mind, Listing 6 shows an updated version of our view code.
Listing 6. Updated view
<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>
|
Instead of having a form, we use a Lift snippet. The lift:Update.show tells Lift we want to invoke a method called show on a class called Update. The qp:update tag output variables bound within that method.
Listing 7. The Update snippet
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>
)
}
}
|
The qpx object is a request variable. The show method is bound to the value submitted in the form. We create a
temp variable that is either an empty string or, if there is
a value submitted to the form, prefixes a Received: string
to the submitted value. The bind section binds data to each
of the tags we saw in the view. The data in this case is XML. The text and submit functions are XML
builders. For the messages tag, we use XML directly. XML is
a first-class citizen in Scala, and Lift takes great advantage of this.
To test the new code, all we need to do is recompile, and once again, we use Maven.
Listing 8. Recompiling the application
$ 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] ------------------------------------------------------------------------
|
We had to recompile because we added a new Scala class. If all we had done was change
the HTML view, there would have been no need to recompile. We do not have to stop and
start the server; we can just leave it run as we recompile. Now our application should look like Figure 4.
Figure 4. ¿Qué pasa? V2.0
Take a quick look at the HTML source code for this page. You will see something similar
to Listing 9, although it will not be exactly the same.
Listing 9. View source excerpt
<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>
|
Notice the obfuscated names of the form fields. This adds security to the form, as it
is much harder to fake a form submission. Lift handles all of this for you. Now that we
have a secure form, let's do something a little more interesting with the data being
submitted from this form. To do that, we need to add a model to our application.
Adding a model
Back in Figure 1, you might have noticed a model directory. This is where we put model
classes in Lift, and there is already one there: User. This
is a general-purpose object model for a Web user you can use as-is, modify, or
discard. Take a look at it because it demonstrates many of the concepts in Lift's OR
mapping. We will create a new model to store updates.
Listing 10. The Message model
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)
}
|
Notice we have a Message class and a Message object. Anything directly declared as an object in Scala is a
singleton. The Message singleton serves as a metadata
repository and factory class for the Message class. Notice
that it extends KeyedMetaMapper object. The square brackets
denote a parameterized class in Scala, and in this case, it takes the type of the ID
and the type of the actual model class. This is a powerful construct that makes the
Message object easy to use.
The Message class simply defines two fields: id and text.
These will be mapped to the database. The types, such as MappedString, not only tell Lift how to map the field to the
database type but also provide HTML rendering for the field. Now we can use the Message model in our snippet.
Listing 11. Snippet with model
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>
)
}
}
|
There are two things added. First, if there is a new message being posted, we create a
new Message model and save it to the database. Next, we load
all the Messages from the database. The foldLeft construct allows us to iterate over the collection of Messages (imagine it going from left to right) and on each
iteration, replace the current expression (which starts as the empty string, the first
parameter passed to foldLeft) with the evaluation of the
closure (the second parameter passed to foldLeft). In this
case, it effectively appends the text of all of the messages together, separating them
with a ### string. Not exactly a fancy way to format the
messages, but it shows how you can use Lift's OR framework, as well as the power of Scala.
Before we recompile the application, there's one more thing that must be done. In the
Boot class, we need to tell the application that there is a
new model for the database. This will create the table in the database. It takes only
one line of code to do this.
Listing 12. Adding a model to the Boot class
Schemifier.schemify(true, Log.infoF _, Message)
|
In the Boot class shown in Listing 13, you can see the logic
it uses to get a database connection. Lift can use a JNDI-supplied connection, using a
convention-over-configuration approach: It automatically looks for a data source bound
to the JNDI name. If there is no JNDI data source, it uses the DBVendor object to manage connections. If you want to use a database
other than the embedded Derby one, you can add the connection information here.
Now we have seen the basic functions in Lift, so let's find out how to deploy it to Geronimo.
Deploying to Geronimo
Scala has a powerful syntax that is much more expressive than Java. Nonetheless, Scala
compiles down to byte code just like Java. In fact, its byte code can be hard to
distinguish from byte code created through Java source code, and its performance is on
par with Java. Lift makes great use of Scala to allow for paradigms that are much
different from your typical Java Web application framework. Still, Lift runs as a Java
Web application. That is what is going on when using the embedded Jetty. You can also
very easily create a Java WAR with Lift that can be deployed to any Web container,
including Geronimo.
Creating a WAR
Before we create a WAR, we want to make it appropriate for use with Geronimo. In the
webapp/WEB-INF directory, we will create a Geronimo deployment plan.
Listing 13. Geronimo deployment plan
<?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>
|
Notice the /quepasa path; we will use that to access our application after it is
deployed to Geronimo. Now we can create a WAR file using Maven.
Listing 14. Create WAR with Maven
$ 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] ------------------------------------------------------------------------
|
When you run this command for the first time, there will be several JARs downloaded.
Now that the WAR has been created, it is ready to be deployed to Geronimo.
Deploying the WAR
Deploying an application to Geronimo is easy to do, and this is no different when using
a Lift Web application. You can use the Geronimo console, shown in Figure 5.
Figure 5. Geronimo console
Use the Archive > Browse button to navigate to the WAR file that Maven
created in Listing 14 and install the application. When it is installed, you should be
able to go to http://locahost:8080/quepasa, as shown below.
Figure 6. Lift running on Geronimo
Everything will work just like it did during testing. From here, you can customize the
application more, adding more pages, models, etc. You can customize its look and feel
by changing the master template. You can also integrate more with Geronimo by creating a database pool bound for Lift to use.
Summary
This article has shown you how to get up and running with Lift. We have seen all of the
essential parts of Lift: its bootstrap class, views, snippets, and models. We have seen
how to use them together with Lift's view-first approach to Web application. And we saw
how easy it is to deploy a Lift application to Geronimo, where we can take advantage of
the many benefits offered by Geronimo. There is a lot we can do already, and Lift is
still very young. Keep your eyes on it as it matures and take advantage of it when you can.
Download | Description | Name | Size | Download method |
|---|
| Sample code | os-ag-lift-quepasa.zip | 12KB | HTTP |
|---|
Resources Learn
Get products and technologies
-
Scala is a general purpose programming language designed to express common programming
patterns in a concise, elegant, and type-safe way.
-
Java technology is a programming language and a
platform. Download the Java Development Kit (JDK) from Sun Microsystems or from IBM.
-
Maven, an Apache project, is a software
project-management and comprehension tool.
-
Geronimo, an Apache project, is a server
runtime framework that pulls together open source alternatives to create runtimes that
meet the needs of developers and system administrators.
-
Apache Derby, an Apache DB subproject, is an
open source relational database implemented in Java.
-
Innovate your next open source development project with IBM trial software, available for download or on DVD.
-
Download IBM product evaluation versions, and get your hands on application development tools and middleware products from DB2®, Lotus®, Rational®, Tivoli®, and WebSphere®.
Discuss
About the author  | 
|  | Michael Galpin has been developing Java software professionally since 1998. He currently works for eBay. He holds a degree in mathematics from the California Institute of Technology. |
Rate this page
|  |