Creating self serving war with embeded jetty

2,238 views
Skip to first unread message

Olek Swirski

unread,
Sep 3, 2012, 5:50:30 AM9/3/12
to lif...@googlegroups.com

Ideally, I would like to have self serving .war with embedded jetty, so that each app is self contained and has its own container - preferably jetty 8. Also I noticed when running jetty from sbt, webapp takes much less memory (not sure why) and my current vps is "economy class" so this matters. This is pretty big difference. I run same app through xsbt and it claimed about 100MB ram. In standalone jetty-hightide-8.1.1.v20120215 it was over 1GB, maybe I messed something up, or default config is bloated. Anyway, I would like to have independent containers to gain flexibility. There is some info on the net on embedding jetty, but I found no working example. I found this plugin for sbt, but it doesn't build and seems a bit outdated too: https://github.com/glenford/xsbt-war-plugins
Maybe someone here already has some experience, how best to do that?
Another approach that comes to my mind, is that I could use sbt on my server, to run each app from independent sbt instance with xsbt-web-plugin, but this adds sbt to the stack (maybe it's nothing wrong). Is it wise to use sbt this way in production (I don't have big traffic) ? I already figured out, how to enforce production mode from the Boot and how to change port number for sbt jetty instance, so maybe it would be ok. I would have to make different proxy pass settings in nginx for each sbt instance, but I guess this can't be avoided with self contained war too. Any hint will be greatly appreciated. Please share your opinions.

Olek Swirski

unread,
Sep 3, 2012, 6:55:37 AM9/3/12
to lif...@googlegroups.com
Looks like one-jar sbt plugin will do what I need
https://github.com/sbt/sbt-onejar

Olek Swirski

unread,
Sep 3, 2012, 7:34:28 AM9/3/12
to lif...@googlegroups.com
well, not exactly. it generates runnable jar, where I can point to a specific Object to be executed
(must have main method). so I guess I still have to put some code into that main, to actually start
jetty, maybe pass some options to it,  and hope it will find webapp ...

Naftoli Gugenheim

unread,
Sep 3, 2012, 8:08:15 AM9/3/12
to lif...@googlegroups.com
I just now spent a lot of time on it and didn't succeed.


--
--
Lift, the simply functional web framework: http://liftweb.net
Code: http://github.com/lift
Discussion: http://groups.google.com/group/liftweb
Stuck? Help us help you: https://www.assembla.com/wiki/show/liftweb/Posting_example_code
 
 
 

Jeppe Nejsum Madsen

unread,
Sep 3, 2012, 9:09:07 AM9/3/12
to lif...@googlegroups.com
I have something running that does just this (it even runs as a
Windows Service :-)

It's a client project so I can't put it on github and don't have much
time atm to extract the bits.

Basically you just create an exploded war file and include the bits
needed to run jetty. You can use the Lift supplied RunWebApp as a
starting point. It will launch the embedded jetty. All that remains is
to pack it into a single archive.

Btw, I used gradle, not sbt.

/Jeppe

Diego Medina

unread,
Sep 3, 2012, 9:33:25 AM9/3/12
to lif...@googlegroups.com
I think that along what Jeppe said, you may be able to use
http://stackoverflow.com/questions/2458440/executable-war-file-that-starts-jetty-without-maven
that is for jetty 6 , and ant, but you can take the jetty 8 code from here:

https://github.com/lift/lift_25_sbt/blob/master/scala_29/lift_basic/src/test/scala/RunWebApp.scala

and once you get it running with ant, you may be able to ask on the
sbt list, how to translate it ti sbt

Regards,

Diego
--
Diego Medina
Lift/Scala Developer
di...@fmpwizard.com
http://www.fmpwizard.com

Olek Swirski

unread,
Sep 3, 2012, 11:50:19 AM9/3/12
to lif...@googlegroups.com
ok, I'll give it a try. I don't feel like I know what I'm doing but at least it looks like
in general this should be feasible, so there is hope ... :-)

Olek Swirski

unread,
Sep 3, 2012, 2:08:58 PM9/3/12
to lif...@googlegroups.com
ok, this is what I have now

to have jetty included in created artifact in build.sbt except from usual
    "org.eclipse.jetty" % "jetty-webapp" % "8.1.0.v20120127" % "container",
    "org.eclipse.jetty" % "jetty-plus" % "8.1.0.v20120127" % "container",
I added also:
    "org.eclipse.jetty" % "jetty-webapp" % "8.1.0.v20120127",
    "org.eclipse.jetty" % "jetty-plus" % "8.1.0.v20120127",

then, I created runnable object

package com.selfserve

import java.io.File

import org.eclipse.jetty.server.Server
import org.eclipse.jetty.webapp.WebAppContext

import net.liftweb.util.Props


object Start {

  def main(args: Array[String]): Unit = {

    val port = Props.getInt("port", 8091)
    val server = new Server(port)
    val domain = Start.getClass.getProtectionDomain
    val location = domain.getCodeSource.getLocation

    val webapp = new WebAppContext
    webapp.setServer(server)
    webapp.setContextPath("/")
    webapp.setDescriptor(location.toExternalForm() + "/WEB-INF/web.xml")
    webapp.setServer(server)
    webapp.setWar(location.toExternalForm())

    // (Optional) Set the directory the war will extract to.
    // If not set, java.io.tmpdir will be used, which can cause problems
    // if the temp directory gets cleaned periodically.
    // Your build scripts should remove this directory between deployments
    webapp.setTempDirectory(new File("/home/olo/dev/webtmp/selfserve"))

    server.setHandler(webapp)
    server.start
    server.join

  }

}

Now, there is a little problem, because example at stackoverflow uses ant to build this.
I would prefer to stay with sbt. So, just to check what effect this changes had, I first
tried using one-jar and pointing to Start as my runnable class. To do that, i followed
the instruction from https://github.com/sbt/sbt-onejar and added  the following to
plugins.sbt:
addSbtPlugin("com.github.retronym" % "sbt-onejar" % "0.8")
because I'm using sbt 0.12 adding resolvers wasn't necessary (but I list it below for reference)
// This resolver declaration is added by default SBT 0.12.x
resolvers += Resolver.url(
  "sbt-plugin-releases", 
  new URL("http://scalasbt.artifactoryonline.com/scalasbt/sbt-plugin-releases/")
)(Resolver.ivyStylePatterns)

What was left to have one-jar work was to add in build.sbt

seq(com.github.retronym.SbtOneJar.oneJarSettings: _*)

mainClass in oneJar := Some("com.selfserve.Start")

Then in sbt
;reload ;one-jar

created artifact is runnable with
java -jar selfserve-webapp_2.9.1.1-one-jar.jar
And it indeed does start jetty on the correct port! It even puts stuff into the
/home/olo/dev/webtmp/selfserve
So far so good, but unfortunately not what should be there, instead I have
/home/olo/dev/webtmp/selfserve/webapp/main/selfserve-webapp_2.9.1-1.1.jar
which is not extracted and only includes directory tree from my webapp with
no files there. looks like, one-jar did it's job, but webapp itself was not build
and not included ...
Still it is kind of prove of concept. So if I get equivalent of ant task in sbt, or
perhaps adjust what one-jar does, then this would work. Already it starts jetty
just jetty can't find the files, because they are not there.

Unfortunately it's been long time since I last used ant, and never was too good
with it, so don't know how to translate it into sbt. Haven't done sbt plugin either.
Any suggestions?

Olek Swirski

unread,
Sep 3, 2012, 6:18:48 PM9/3/12
to lif...@googlegroups.com
i think i need to do some serious work on my sbt skills. just made first step
by reading the getting started manual. i did read it quite some time ago but
didn't remember much above absolute minimum. this time it think i grasped
it. must say this whole sbt thing is pretty impressive.

Olek Swirski

unread,
Sep 4, 2012, 1:52:29 AM9/4/12
to lif...@googlegroups.com
<--- !!! SOLVED !!! --->

HERE IS A STEP BY STEP INSTRUCTION

1. install sbt-assembly plugin, easy to follow instruction is here
https://github.com/sbt/sbt-assembly

2. in build.sbt set main class matching your executable object from point 3.
mainClass in assembly := Some("com.selfserve.Start")

make sure jetty is not stripped from compile (most of the time jetty libs
will only be used in (test,container) so you may do something like this
in your build.sbt:

libraryDependencies ++= Seq(
        // -- jetty related --
    "javax.servlet" % "servlet-api" % "2.5" % "provided",
    "org.eclipse.jetty" % "jetty-webapp" % "8.1.0.v20120127" % "container,test,compile",
    "org.eclipse.jetty" % "jetty-plus" % "8.1.0.v20120127" % "container,test,compile"
)

for some reason some latest versions of jetty artifact throw sbt error, but the one above is ok.


3. add executable object like one below to your lift application


package com.selfserve

import java.io.File
import org.eclipse.jetty.server.Server
import org.eclipse.jetty.webapp.WebAppContext
import net.liftweb.util.Props

object Start {

  def main(args: Array[String]): Unit = {
   
    /* choose different port for each of your webapps deployed on single server
     * you may use it in nginx proxy-pass directive, to target virtual hosts */
    val port = Props.getInt("port", 8090)

    val server = new Server(port)
    val domain = Start.getClass.getProtectionDomain
    val location = domain.getCodeSource.getLocation

    val webapp = new WebAppContext
    webapp.setServer(server)
    webapp.setContextPath("/")
    /* use resource base to avoid mixing your webapp files on the top level
     * of the executable jar, with all the included libraries etc
     * here I used webapp dir as it matches target dir of package-war task and makes
     * merging of webapp dir with output of assembly easier */
    webapp.setResourceBase("webapp")
    /* also include webapp dir in path to web.xml */
    webapp.setDescriptor(location.toExternalForm() + "/webapp/WEB-INF/web.xml")

    webapp.setServer(server)
    webapp.setWar(location.toExternalForm())

    // (Optional) Set the directory the war will extract to.
    // If not set, java.io.tmpdir will be used, which can cause problems
    // if the temp directory gets cleaned periodically.
    // Your build scripts should remove this directory between deployments
    webapp.setTempDirectory(new File("/home/olek/webtmp/selfserve"))

    server.setHandler(webapp)
    server.start
    server.join
  }
}

4. in sbt
;assembly ;package-war
in target dir, this will create
selfserve.com-assembly-1.0.jar
and webapp directory

5. almost there, we can already run
java -jar selfserve.com-assembly-1.0.jar
it has all the classes and libs but no html, images etc, and no WEB-INF/web.xml (so no lift filter)
this is easily fixed with zip. cd to target directory and run:
zip -r selfserve.com-assembly-1.0.jar webapp -x webapp/WEB-INF/classes\* webapp/WEB-INF/lib\*
make sure you actually are in target dir, when runnig the command above, or paths will be messed
up inside executable jar.

6. run
java -jar selfserve.com-assembly-1.0.jar
webapp.setTempDirectory setting from 3. will determine where jar it is extracted

7. visit localhost:8090 (or whatever port you defined in Start) and enjoy
your standalone, self serving lift webapp :) you now can run multiple instances
on a single machine, each will have it's own container. you must use a different
port number for every embedded server. you may even run or more instances of the
same app (just change port number) to have a fallback / load balancing.

8. to run your app in production mode include this in your Boot.scala
before Boot class

import javax.servlet.FilterConfig
import net.liftweb.http.LiftFilter
import net.liftweb.util.Props

class RunModeLiftFilter extends LiftFilter {
  override def init(config: FilterConfig) {
    System.setProperty("run.mode", "production")
    /* System.setProperty("run.mode", "development") */
    super.init(config)
  }
}

I used sbt 0.12, jetty 8.1.0.v20120127 and lift 2.5-SNAPSHOT on
java-7-openjdk but should work with other setups most of the time
just jetty version is one I would leave unchanged, as newer have
some dependency problems in sbt - anyone got that fixed??

Diego Medina

unread,
Sep 4, 2012, 2:03:55 AM9/4/12
to lif...@googlegroups.com
Thanks for the write up!

Here is how to get latest jetty in sbt

https://github.com/harrah/xsbt/issues/499

Thanks

Diego

Olek Swirski

unread,
Sep 4, 2012, 2:44:10 AM9/4/12
to lif...@googlegroups.com
thx Diego, I tried out jetty sbt fix you linked, but didn't work for me,
did it work for you?

oh well, looks like 8.1.0.v20120127 will be my friend for another
couple of months :)

Olek Swirski

unread,
Sep 4, 2012, 4:23:27 PM9/4/12
to lif...@googlegroups.com
IMPORTANT UPDATE - point 3 was incorrect and worked only because of a name
clash/coincidence still using webapp from target dir. Use the code below instead.


package com.selfserve

import java.io.File
import org.eclipse.jetty.server.Server
import org.eclipse.jetty.webapp.WebAppContext
import net.liftweb.util.Props
import net.liftweb.common._

object Start extends Logger{


  def main(args: Array[String]): Unit = {
    /* choose different port for each of your webapps deployed on single server
     * you may use it in nginx proxy-pass directive, to target virtual hosts
     * line below tries to read it from properties file with fallback 8090 */

    val port = Props.getInt("port", 8090)
    val server = new Server(port)
    val webctx = new WebAppContext
    /* use embeded webapp dir as source of the web content -> webapp
     * this is the dir within jar where we have put stuff with zip.
     * it was in a directory created by package-war, in target (also
     * named webapp), which was outside the jar. now, thanks to zip
     * it's inside so we need to use method bellow to get to it.
     * web.xml is in default location, of that embedded webapp dir,
     * so we don't have do webctx.setDescriptor */
    val webappDirInsideJar = webctx.getClass.getClassLoader.getResource("webapp").toExternalForm
    webctx.setWar(webappDirInsideJar)
    /* might use use external pre-existing webapp dir instead of referencing
     * the embeddded webapp dir but it's not very useful. why would we put webapp inside if we end up
     * using some other external directory. I put it for reference, may make sense under some circumstances.
     * webctx.setResourceBase("webapp")
     * */
   
    webctx.setContextPath("/")
    /* optionally extract embedded webapp to specific temporary location and serve from there */
    val webtmpdir = "/home/olek/dev/webtmp/selfserve"
    webctx.setTempDirectory(new File(webtmpdir))
   
    server.setHandler(webctx)
    server.start
    server.join

  }

}

Olek Swirski

unread,
Sep 4, 2012, 4:33:26 PM9/4/12
to lif...@googlegroups.com
also 'extends Logger' is not necessary if we do no logging,
so can be left out in this case

On 04/09/12 22:23, Olek Swirski wrote:
> extends Logger

Maarten Koopmans

unread,
Sep 5, 2012, 4:15:07 PM9/5/12
to lif...@googlegroups.com
I got almost there 1.5 years ago (see https://github.com/mbk/Lift-standalone), but your write-up seems pretty accurate and current.

Thanks!!

--Maarten

On Wednesday, September 5, 2012, vsumner wrote:
Thanks for this!

Does this bypass the lift bootstrap? 
--

Olek Swirski

unread,
Sep 5, 2012, 8:02:41 PM9/5/12
to lif...@googlegroups.com
the boot is called just when the embedded jetty starts to operate,
so should work as usual. there may be one thing still to be fixed,
haven't pinned it down yet, but I had the impression that embedded
jetty didn't read files from the props dir ... I mean, I noticed it kind
of ignored my logback settings, which are there. so this is something
I need to verify.

Diego Medina

unread,
Sep 16, 2012, 6:39:43 PM9/16/12
to lif...@googlegroups.com
You can avoid the zip step by adding this to your build.sbt file:



resourceGenerators in Compile <+= (resourceManaged, baseDirectory) map
{ (managedBase, base) =>
val webappBase = base / "src" / "main" / "webapp"
for {
(from, to) <- webappBase ** "*" x rebase(webappBase, managedBase /
"main" / "webapp")
} yield {
Sync.copy(from, to)
to
}
}



then assembly will include the webapp folder in the jar (just tried it)

I'll play around with this a bit more and then I'll add it to the
wiki, thanks Olek for looking into it!

Diego

Olek Swirski

unread,
Sep 16, 2012, 7:57:20 PM9/16/12
to lif...@googlegroups.com
Diego,
there is one thing ..., in webapp inside WEB-INF, there are libs (jars) and
classes, which are already taken care of by assembly. so to avoid
almost doubling the size of the end product, this needs to be excluded.

Olek Swirski

unread,
Sep 16, 2012, 8:09:05 PM9/16/12
to lif...@googlegroups.com
ok, seems it's not duplicated. and it looks like the files from
props dir go into right place.

Olek Swirski

unread,
Sep 16, 2012, 8:23:57 PM9/16/12
to lif...@googlegroups.com
ups, I forgot to do reload in sbt. there is no duplication, but the thing
with props is not quite solved. I even got it straight once, but it was
late and I forgot what I did. just need to rename the dir I think.
I will check that.

On 17/09/12 01:57, Olek Swirski wrote:

Olek Swirski

unread,
Sep 16, 2012, 9:22:07 PM9/16/12
to lif...@googlegroups.com
ok, so my observation is that it does read default.props or
production.default.props as it should. but, it seems not to
read default.logback.xml or
production.default.logback.xml
and default logging level debug is used, so lots of output
when starting jetty and then with each request.

Diego Medina

unread,
Sep 16, 2012, 10:10:31 PM9/16/12
to lif...@googlegroups.com
I use a file called

logback.xml

and it is being read, because the log only shows this one log entry I
have in info level, which is what happens when I run from sbt too.

Olek Swirski

unread,
Sep 16, 2012, 10:27:49 PM9/16/12
to lif...@googlegroups.com
I have set INFO level, but still lots of DEBUG messages show up,
which does not happen when run from sbt. Diego, could you
tell precisely in which dir you hold your logback.xml? I have
put it now in src/main/resources/props, now also added
logback.xml (earlier I had default.logback.xml and
production.defaust.logback.xml). Still same thing happens.

Olek Swirski

unread,
Sep 16, 2012, 10:29:34 PM9/16/12
to lif...@googlegroups.com
What is your logging lib, I'm using this one:
"ch.qos.logback" % "logback-classic" % "1.0.6", // Logging


On 17/09/12 04:10, Diego Medina wrote:

Diego Medina

unread,
Sep 16, 2012, 10:31:41 PM9/16/12
to lif...@googlegroups.com
I have my logback.xml file ins:

src/main/resources

(note, it is not in the props subdirectory)

my dependencies are:

"ch.qos.logback" % "logback-classic" % "0.9.26",
"org.slf4j" % "jcl104-over-slf4j" % "1.5.11",


( ise use "org.slf4j" % "jcl104-over-slf4j" % "1.5.11",
because I'm using shiro, which needs "org.slf4j" %
"jcl104-over-slf4j" % "1.5.11", to work well)

Hope that helps.

Diego

Olek Swirski

unread,
Sep 16, 2012, 10:47:17 PM9/16/12
to lif...@googlegroups.com
ok, got it. turns out, when jetty runs in embedded mode
it will only read logback.xml (and no default.logback.xml
or production.default.logback.xml) from resources dir.
also does not read from props subdir. this is different than
with jetty standalone or when used from sbt, which do read
from props dir, and also filenames with prefixes. thank you for
help Diego, it already took me much time and would take me
ages to figure this out on my own.

Olek Swirski

unread,
Sep 16, 2012, 10:49:57 PM9/16/12
to lif...@googlegroups.com
also I got confused, because props.xml files were
read ok from props dir, and also with prefixes

Diego Medina

unread,
Sep 16, 2012, 10:50:41 PM9/16/12
to lif...@googlegroups.com
glad you got it working, what I like the most about this setup is that
I don't need to worry about the xml files to configure a standalone
jetty..

Olek Swirski

unread,
Sep 16, 2012, 10:59:14 PM9/16/12
to lif...@googlegroups.com
I got this idea (nothing revolutionary but still :), under nginx
I can do seamless reload of app. I start new instance under
different port number and have 2 running. then change
port number in nginx and do a config reload with
sudo kill -HUP $(cat /usr/local/nginx/logs/nginx.pid)

now I'm thinking if it would be possible to do the switch
without even loosing some active sessions. perhaps
add a header to each request going from the nginx with
info from which port it was served and always passing
this back and forth. then when I would want to kill old
instance and use new, I could identify old sessions
and route them to old instance (old port) and all the
new ones to new instance. then after some grace
period, could safely shut down old instance.

Diego Medina

unread,
Sep 17, 2012, 12:02:05 AM9/17/12
to lif...@googlegroups.com
You may be able to use this module
http://code.google.com/p/nginx-sticky-module/

Diego

Maarten Koopmans

unread,
Sep 17, 2012, 8:38:25 AM9/17/12
to lif...@googlegroups.com


On Monday, September 17, 2012, Diego Medina wrote:
You can avoid the zip step by adding this to your build.sbt file:



resourceGenerators in Compile <+= (resourceManaged, baseDirectory) map
{ (managedBase, base) =>
  val webappBase = base / "src" / "main" / "webapp"
  for {
    (from, to) <- webappBase ** "*" x rebase(webappBase, managedBase /
      "main" / "webapp")
  } yield {
    Sync.copy(from, to)
    to
  }
}



then assembly will include the webapp folder in the jar (just tried it)

I'll play around with this a bit more and then I'll add it to the
wiki, thanks Olek for looking into it!


Wow, where did you pull that code from? Looks almost from another planet. The funny thing is, I get what it does.

Olek Swirski

unread,
Sep 17, 2012, 8:41:56 AM9/17/12
to lif...@googlegroups.com
for reference, I assembled tiny working example using embedded
jetty and squeryl-record + db configuration stuff and h2 console
https://github.com/oolekk/sqrlrcrd.com

Diego Medina

unread,
Sep 17, 2012, 2:24:31 PM9/17/12
to lif...@googlegroups.com
>
> Wow, where did you pull that code from? Looks almost from another planet.
> The funny thing is, I get what it does.

I wish I could say I came up with it on my own, but no :)
it is from the sbt list :)
https://groups.google.com/d/topic/simple-build-tool/PRTzsnGj07k/discussion


Regards,

Diego

Peter Petersson

unread,
Sep 17, 2012, 5:11:16 PM9/17/12
to lif...@googlegroups.com
Nice to see MyShemaHelper and related stuff getting a new life :)
Great work Olek!

Best regards
   Peter karma4u101 Petersson

Olek Swirski

unread,
Sep 18, 2012, 4:16:02 AM9/18/12
to lif...@googlegroups.com
thx! I just added README.md and pointed to your example
in the 'credits' paragraph.
https://github.com/oolekk/sqrlrcrd.com

Maarten Koopmans

unread,
Sep 18, 2012, 4:25:02 AM9/18/12
to lif...@googlegroups.com
Yes, great work indeed :-) I always like things that are close to
being "one executable".

Olek Swirski

unread,
Sep 20, 2012, 11:31:34 AM9/20/12
to lif...@googlegroups.com
I'm glad you found it useful.

Now, there is one more pretty unintuitive thing to consider, when
it comes to the resolution of props files and logging configurations.
I noticed, that no matter what run mode I set in
Boot.scala in this section - let's say I set it to production, like so:
class RunModeLiftFilter extends LiftFilter {
override def init(config: FilterConfig) {
System.setProperty("run.mode", "production")
super.init(config)
}
}
always props/default.props.xml will be read and not production.default.props
But app did in fact run in production mode (templates were cached).
To read the matching props file I had to do the similar setting in
Start.scala:
System.setProperty("run.mode","production")

Then I did experiment a bit, to have clear picture of where do the logging
and props settings come from in different scenarios - i.e. when runing
executable jar vs running from sbt or war. It turned out to be quite
complicated. This is what I discovered (I added comments in github
repo https://github.com/oolekk/sqrlrcrd.com to make that clear):

When running your app from sbt or normal war (but not from executable jar)
only mode setting from Boot.scala is taken into consideration, to decide
run.mode and which props files to read.
1) Run mode setting from Boot.scala will decide which props file to read:
"run.mode" "development" -> resources/props/default.props.xml
"run.mode" "production" -> resources/props/production.default.props.xml
... some other possible run modes (staging, test)
2) Run mode setting from Boot.scala will decide which config
file for logging to read:
"run.mode" "development" -> resources/props/default.logback.xml
"run.mode" "production" -> resources/props/production.default.logback.xml
.... some other possible run modes (staging, test)
3) Run mode setting from Boot.scala will decide which mode of operation
will lift use, for example:
"run.mode" "development" -> watch for templates changes and reload them
"run.mode" "production" -> cache templates, ignore changes

It's a quite different story when your app is run from executable jar.
Then the situation looks like this:
1) Run mode setting from Start.scala will decide which props file to read:
(if you don't set this at all in Start.scala development mode will be used)
"run.mode" "development" -> resources/props/default.props.xml
"run.mode" "production" -> resources/props/production.default.props.xml
Setting in Boot.scala no longer decides which props file to read.
2) Completely ignored are the logging settings from
resources/props/default.logback.xml
resources/props/production.default.logback.xml
or some other possible run modes (staging, test)
instead resources/logback.xml file is read to determine logging behaviour,
no matter what run mode is chosen either in Boot or Start
3) IMPORTANT AND A BIT SURPRISING - this stays unaffected by run mode
setting specified in Start
Run mode setting from Boot.scala will decide which mode of operation
will lift use, for example:
"run.mode" "development" -> watch for templates changes and reload them
"run.mode" "production" -> cache templates, ignore changes

So settings in Boot.scala and Start.scala do not have to match, and do
different things. When jetty is run from sbt, or when your app is run
from standard war only settings in Boot.scala have any impact. But when
running an executable jar, settings from both Boot.scala and Start.scala
are important. You could possibly set run mode to development in
Start.scala,
but to production in Boot.scala and have situation, where props are read
from resources/props/deafault.props.xml, logging settings are read from
resources/logback.xml, and your app still runs in production mode internally
and caches templates. In production you will most probably want to set
run mode to production both in Boot.scala and in Start.scala

Diego Medina

unread,
Sep 20, 2012, 11:42:03 AM9/20/12
to lif...@googlegroups.com
What I'm doing in this case is not to hardcode the settings, but to
pass them fron the terminal / script that starts my jar, so I run:

java -Drun.mode=production -jar myjarname.jar

I actually use nohup so I run it as:

nohup java -Drun.mode=production -jar myjarname.jar >
/dev/null 2> /dev/null &

and then I can log off from the remote terminal on my server and my
Lift app stays running.

Regards,

Diego

Olek Swirski

unread,
Sep 20, 2012, 12:36:09 PM9/20/12
to lif...@googlegroups.com
You are right Diego. This simplifies a lot.
I used this run mode filter in Boot:

class RunModeLiftFilter extends LiftFilter {
override def init(config: FilterConfig) {
System.setProperty("run.mode","production")
super.init(config)
}
}

on old shared server, where I couldn't control java options.
This allowed me to set execution mode for a war. But now
I don't need it and it only complicates things, as by setting
run mode directly for java I get what I want - mode of lift
operation is set and corresponding props file is read.
So I forgot what this was needed for and got stuck with
that configuration from my old setup which now does not
really serve any purpose. I will get rid of that excessive
stuff, and correct github example, thx Diego!

Anyway, there is still this thing about logback configs.
Would be nice, if with executable jar, still these prefixed
logback configs could be read depending on current
run mode - same as it works with props. As it is now always
resources/logback.xml is read instead no matter the run
mode. But it's not a big deal, because I can always copy
one of the configs from props, or just always use same setting
(well not really good idea). So it's nothing urgent but if someone
finds the way to have this fixed, please let me know :)

On 20/09/12 17:42, Diego Medina wrote:
> java -Drun.mode=production

Diego Medina

unread,
Sep 20, 2012, 1:18:57 PM9/20/12
to lif...@googlegroups.com

Cool, about logback, it is not lift, but logback itself the one who decides which xml to read, maybe they use a different-D option

Diego

Diego
Sent from my android cell

Olek Swirski

unread,
Sep 20, 2012, 1:48:10 PM9/20/12
to lif...@googlegroups.com
The funny thing is, when I was setting run mode in Boot,
it had this way of working, that even when run from sbt,
you can enforce production mode. Logback would then
read the correct file from props, depending on the mode
"development" -> props/default.logback.xml
"production" -> props/production.logback.xml
...
I think same thing applied to a regular war using this in
Boot setting. So this is why I think it should also work
that way with executable jar.

Olek Swirski

unread,
Sep 20, 2012, 1:50:43 PM9/20/12
to lif...@googlegroups.com
should be
"development" -> props/default.logback.xml
"production" -> props/default.production.logback.xml

Olek Swirski

unread,
Sep 20, 2012, 1:52:32 PM9/20/12
to lif...@googlegroups.com
wrong again :)
"development" -> props/default.logback.xml
"production" -> props/production.default.logback.xml

Olek Swirski

unread,
Sep 20, 2012, 3:00:21 PM9/20/12
to lif...@googlegroups.com
ok, I asked at the logback mailing list how to make it use
different config depending on run.mode


On 20/09/12 19:18, Diego Medina wrote:

Diego Medina

unread,
Sep 20, 2012, 4:39:06 PM9/20/12
to lif...@googlegroups.com
On Thu, Sep 20, 2012 at 3:00 PM, Olek Swirski <oleks...@gmail.com> wrote:
> ok, I asked at the logback mailing list how to make it use
> different config depending on run.mode

nice, I look forward to knowing what they say, thanks for all the work
you are doing on this topic

Olek Swirski

unread,
Sep 20, 2012, 5:49:00 PM9/20/12
to lif...@googlegroups.com
thx Diego :)
i got hint from ceki at logback mailing list and based on it I could
quite easily point logback to the run.mode dependent config.
just need to add this few lines at the beginning of Start.scala:

/* Calculate run.mode dependent path to logback configuration file.
* Use same naming scheme as for props files. */
val logbackConfFile = {
val propsDir = "props"
val fileNameTail = "default.logback.xml"
val mode = System.getProperty("run.mode")
if (mode != null) propsDir + "/" + mode + "." + fileNameTail
else propsDir + "/" + fileNameTail
}
/* set logback config file appropriately */
System.setProperty("logback.configurationFile", logbackConfFile)

Olek Swirski

unread,
Sep 20, 2012, 5:58:34 PM9/20/12
to lif...@googlegroups.com



On 20/09/12 23:49, Olek Swirski wrote:
> /* Calculate run.mode dependent path to logback configuration file.
> * Use same naming scheme as for props files. */
> val logbackConfFile = {
> val propsDir = "props"
> val fileNameTail = "default.logback.xml"
> val mode = System.getProperty("run.mode")
/* maybe this is a tad better: */
if(mode == null || mode == "development") propsDir + "/" +
fileNameTail
else propsDir + "/" + mode + "." + fileNameTail

Olek Swirski

unread,
Sep 20, 2012, 6:15:12 PM9/20/12
to lif...@googlegroups.com
It seems default mode is not really
called "development" , it just has no name.
So first attempt was actually correct:

if (mode != null) propsDir + "/" + mode + "." + fileNameTail
else propsDir + "/" + fileNameTail

I just pushed this to the git repo and set logback level to
test -> debug
default/development -> info
production -> warn
to demonstrate how it works

Olek Swirski

unread,
Sep 23, 2012, 6:38:21 PM9/23/12
to lif...@googlegroups.com
I just added a very useful feature to my github example. I added
function to attach arbitrary directory under some context, and serve
it from jetty, even if its completely outside of jetty work dir or outside
webapp. This is very useful, if you for example have big dir with
pictures, which change frequently, and also should not go inside the
jar due to its size. Also, passing some flags enable serving directory
listing pages, another flag enables traversal of symbolic links.

I made some refactoring so code is much cleaner too. Also added better
coments and improved readme a lot. Anyone interested please take a look:
https://github.com/oolekk/sqrlrcrd.com

Olek Swirski

unread,
Sep 23, 2012, 6:50:41 PM9/23/12
to lif...@googlegroups.com
Most improvements went into Start.scala , comments in that file should make
everything clear.
https://github.com/oolekk/sqrlrcrd.com/blob/master/src/main/scala/bootstrap/liftweb/Start.scala
--

Diego Medina

unread,
Sep 23, 2012, 7:20:26 PM9/23/12
to lif...@googlegroups.com

One thing I haven't figured out yet is a way to modify a props file after I made my jar file. I know I can read system properties, but I would like to just modify my file, until I find the option I like.

Thanks

Diego
Sent from my android cell

Olek Swirski

unread,
Sep 23, 2012, 7:37:42 PM9/23/12
to lif...@googlegroups.com
you can do this with zip.
the thing is before you use zip, this new file must be under props directory, because
it will be put under the same dir by zip. so because inside the jar it is under props, you
have to be in a dir, frow which path to your props file is like so (don't go inside the props)
props/default.props
so what I do is cd to:
src/main/resources
and then:
zip ../../../target/sqrlrcrd.com-lift-assembly-1.0.jar props/default.props

Olek Swirski

unread,
Sep 23, 2012, 7:40:19 PM9/23/12
to lif...@googlegroups.com
you should then see something like
updating: props/default.props (deflated 50%)
sometimes there may be a warning about some CRC mismatch,
I then just repeat same thing and the warning goes away. so it's
pretty lightweight operation, takes maybe 5 seconds, much less
than normal build and packaging of the jar by assembly


On 24/09/12 01:20, Diego Medina wrote:

Olek Swirski

unread,
Sep 23, 2012, 7:46:31 PM9/23/12
to lif...@googlegroups.com
in fact any file can be replaced that way. to replace all the files
recursively within a directory, for example to replace all files from
props dir, you would go like this:

zip -r ../../../target/sqrlrcrd.com-lift-assembly-1.0.jar props

Diego Medina

unread,
Sep 24, 2012, 9:27:48 AM9/24/12
to lif...@googlegroups.com
On Mon, Sep 24, 2012 at 5:46 AM, ldf <oldl...@googlemail.com> wrote:
> Olek, thanks very much for your embedded Jetty work!
>
> I've tried to apply it to my project and it works well apart from one thing.
> I rely on getClass.getResource(".") to a path so that I can load a locally
> stored WSDL file. This works fine when running in an external container
> (Resin or Jetty) but always returns null when using embedded Jetty. Have
> you encountered this and perhaps found a solution?
>

Do you have your WSDL files in your project at the time you make your
jar file? I load data from a csv file, and what Idid was to put it
inside src/main/resouces/db

and then the path I use from the scala code is something like

db/mycsvfile.csv

Hope that helps.

Diego


> Thanks,
> Lance

ldf

unread,
Sep 24, 2012, 10:00:34 AM9/24/12
to lif...@googlegroups.com
Thanks Diego, I'll give that a try.
Reply all
Reply to author
Forward
0 new messages