Jade Dungeon

lift布置

Jetty

Notes about Jetty in production

Temporary directory

If your JS and CSS files go missing, perhaps only after a few weeks, it?s probably because Jetty unpacks WAR files into a temporary space, and if that space is /tmp it could be automatically cleaned out from time to time (e.g., by the operating system).

Two fixes to choose from:

From Olek: To override this default seting edit jetty/contexts/yourapp.xml by adding line like this:

<Set name="tempDirectory">/some/other/directory/instead/of/default/tmp</Set>

in the section below

<!-- Optional context configuration                                  -->

From Diego: Create a folder named work in the jetty directory and then jetty will not use /tmp, but instead the this new folder.

Full example

This example is a webapps/root.xml for Jetty 9.0.7 that uses a work directory in the Jetty home dir:

    <?xml version="1.0"?>
    <!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
    <Configure id='wac' class="org.eclipse.jetty.webapp.WebAppContext">
    	<Set name="contextPath">/</Set>
    	<Set name="war"><Property name="jetty.webapps"/>/root.war</Set>
    	<Set name="extractWAR">true</Set>
    	<Set name="tempDirectory"><Property name="jetty.home" default="."/>/work</Set>
    </Configure>

See:

Tomcat

Notes about Tomcat in production

Tomcat 7

Tomcat does not parse parameters for request.getParameters for any request other than POST by default.

From the Tomcat config guide

http://tomcat.apache.org/tomcat-7.0-doc/config/http.html

parseBodyMethods A comma-separated list of HTTP methods for which request bodies will be parsed for request parameters identically to POST. This is useful in RESTful applications that want to support POST-style semantics for PUT requests. Note that any setting other than POST causes Tomcat to behave in a way that goes against the intent of the servlet specification. The HTTP method TRACE is specifically forbidden here in accordance with the HTTP specification. The default is POST

See mailing list on no param in production server https://groups.google.com/d/topic/liftweb/VCIoCuHIJJQ/discussion

Tomcat 6 and 7

Tomcat does not permit %2F in URLs by default

You have URLs with %2F (encoding of /) in them, and requests are resulting in 400 (bad request) errors. This is because Tomcat rejects URLs like this for security reasons. To enable %2f in URLs set the following system property:

-Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true

Thank you Torsten for figuring this out!

See also:

Problems shutting down Lift Scheduler

If you see?

SEVERE: The web application [] appears to have started a thread named [Lift Scheduler] but has failed to stop it. This is very likely to create a memory leak.

?in Boot set:

net.liftweb.actor.ThreadPoolRules.nullContextClassLoader = true

See:

Infinite Redirect (Scenario 1)

Solution: Edit src/main/webapp/WEB-INF/web.xml and change

	<filter-mapping>
		<filter-name>LiftFilter</filter-name>
		<url-pattern>/</url-pattern>
	</filter-mapping>

to

	<filter-mapping>
		<filter-name>LiftFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

Discussion: Mailing list discussion of ?Infinite Redirect ? Elastic Beanstalk? https://groups.google.com/forum/?fromgroups#!topic/liftweb/_e_LFsIGTEg

Infinite Redirect (Scenario 2)

Problem: Tomcat, before passing any requests to Lift, will look at your webapp directory (ie. the root of your WAR file). If it sees a directory that matches the URL path then it creates a redirect to the same URL with a trailing slash. (bug)

Problem Example.

If you have a sitemap like this:

val X1 = Menu(Loc("X1", List("example"), "X"))
val X2 = Menu.param[String]("X2", "X", param => Full(param), t => t) / "example" / *

and your templates are like this:

src/main/webapp/example.html
src/main/webapp/example/star.html
src/main/webapp/WEB-INF/web.xml
...

then you would expect http://blah/example and http://blah/example/abc123 to work. Instead when Tomcat gets a request for /example it sees that a directory called example exists (the one containing star.html), and responds with a redirect to /example/. When that request hits Lift, it tries to render the example/star.html template with an empty-string param. In other words, it is impossible to reach the example.html template.

Workaround: Get rid of the example directory. It doesn?t have to affect your URL structure. Append >> TemplateBox(() => Templates("newTemplateName"
Nil)) to your sitemap menu declaration to maintain the URL but point to a different template file.

Workaround Example.

The amended sitemap would be:

val X1 = Menu(Loc("X1", List("example"), "X"))
val X2 = Menu.param[String]("X2", "X", param => Full(param), t => t) / "example" / * >> TemplateBox(() => Templates("example_star" :: Nil))

and the amended templates would be:

src/main/webapp/example.html
src/main/webapp/example_star.html
src/main/webapp/WEB-INF/web.xml
...

PostgreSQL

Installing Jetty and PostgreSQL

First, you?ve to add the Multiverse Repositories to /etc/apt/sources.list, to get Jetty.

# apt-get update
# apt-get install jetty
# apt-get install postgresql-8.3

Firewall Configuration

By default, PostgreSQL 8.3 listens on port 5432 for incoming client connections.

# iptables -A INPUT -s 127.0.0.1/32 -p tcp -m tcp --dport 5432 -j ACCEPT

Create PostgreSQL Database and User

Make sure that PostgreSQL is running. By default, a UNIX user ?postgres? is created during the installation. You can use this user to create/drop databases and/or create local database users. Before we start, make sure that PostgreSQL is running:

# /etc/init.d/postgresql-8.3 start

Become user postgres:

# su postgres

Create a new database:

# createdb liftdatabase

Start the PostgreSQL command prompt and create a new user:

# psql
postgres=# create user george password 'topsecret';
postgres=# \q

PostgreSQL will answer with ?CREATE ROLE?

The next step needs to done on your development machine:

Configure Lift to use PostgreSQL

There?s another article about PostgreSQL here. In your Lift project you?ve to edit the file src/main/resources/props/default.props. Add the following entries:

db.driver=org.postgresql.Driver
db.url=jdbc:postgresql:liftdatabase
db.user=george
db.password=topsecret

In your Boot.scala, look for something like

val vendor = new StandardDBVendor(Props.get("db.driver") openOr "org.postgresql.Driver",
			     Props.get("db.url") openOr "jdbc:postgresql:mydatabase",
			     Props.get("db.user"), Props.get("db.password"))

When you start your application, Lift will try to read the values from the default.props like db.driver, db.url and so on. If the default.props file does not exist, Lift will use the default values provided by openOr.

Now, add the dependencies for PostgreSQL 8.3 to you pom.xml file

<dependency>
   <groupId>postgresql</groupId>
   <artifactId>postgresql</artifactId>
   <version>8.3-606.jdbc4</version>
</dependency>

jetty

firewall

By default, Jetty 5.1 listens by default on port 8280.

# iptables -A INPUT -p tcp -m tcp --dport 8280 -j ACCEPT

Create WAR file and put it on the server

Go to the directory which contains your Lift project and do

# mvn clean install
# mvn package

This will compile a new and clean version of your application and create a .war file (with all dependencies) in the target sub-directory. Copy the .war file to your remote server e.g. you can do that with sftp alias SSH2.

Again, change to your production system.

WAR file and Jetty

Jetty has a directory where its looking for .war files on startup. On Ubuntu Server 8.04, this directory is /usr/share/jetty/webapps. Copy your .war there. Next do

# /etc/init.d/jetty stop
# /etc/init.d/jetty start

If you make any changes to this directory, Jetty will not notice it until you restart it!

Jetty will take some time to start, in my configuration 30 seconds or more were normal, so don?t get nervous to soon?

After it has started successfully you can visit your app at http://yourdomain:8280/your-app, where your-app is the name of the .war file (with .war being omitted).

In case it doesn?t start, you?ve to check the Jetty log files in /var/log/jetty/.

Custom CSS

In case you use your own CSS file with your custom design, you might wonder why its not loading when you use Jetty. I had this kind of problem, because the root of the application changed which lead to a wrong path to the CSS file. In my src/main/webapp/templates-hidden/default.html file I referenced the CSS file like that:

<head>
...
<link href="/static/design.css" rel="stylesheet" type="text/css" />
...
</head>

This means, that the design.css file was located in the static folder of the application. Now, to solve the problem, you?ve to add a LiftRule inside the boot() method of your Boot class. This might look like this:

LiftRules.fixCSS("static" :: "design" :: Nil, Empty)

Explanation: The List parameter for the method fixCSS defines the path to your CSS file, where ?static? is a sub-folder under the root folder and ?design? is a file with the extension .css inside that folder. This path has to match the path you defined in your default.html file! For more information have a look at the Ligt Book, page 229 ff.

done.