Jakub is a Java EE developer since 2005 and occasionally a project manager, working currently with Iterate AS. He's highly interested in developer productivity (and tools like Maven and AOP/AspectJ), web frameworks, Java portals, testing and performance and works a lot with IBM technologies. A native to Czech Republic, he lives now in Oslo, Norway. Jakub is a DZone MVB and is not an employee of DZone and has posted 155 posts at DZone. You can read more from them at their website. View Full User Profile

Running A Leiningen/Ring Webapp As A Daemon Via Upstart (Ubuntu)

07.30.2013
| 955 views |
  • submit to reddit

Running a Java/Clojure app as a daemon on Linux used to be hard but is pretty simple with Ubuntu Upstart (docs). The short story:

  1. Create an all-in one uberjar via “lein with-profile production ring uberjar” (using the lein-ring plugin; a simple lein uberjar would suffice for an app with a main- method)
  2. Create an upstart <service name>.conf file in /etc/init/
  3. Run sudo start/stop/status <service name>

And of course it works with Puppet too.

Example /etc/init/mongodiffer.conf

Example Upstart config file for the service mongodiffer:

## Upstart config file (use 'start mongodiffer', 'stop mongodiffer')
## Note: Stdout and stderr will be captured in /var/log/upstart/mongodiffer.log
## (aside of the native log in /var/log/mongodiffer.log)
author "Jakub Holy"
description "Start the MongoDiffer webapp on its default port (80)"
start on (local-filesystems and net-device-up IFACE!=lo)
# Note: "start on runlevel [2345]" would also do but I want to be explicit that
# running it w/o network is meaningless
# Must run as root to be able to run on port 80; ugly but quick
#setuid mongodiffer
#setgid mongodiffer
exec java -jar /srv/mongodiffer/clj-analytics-mongodiffer-standalone.jar
## TODO: Consider enabling respawning
# respawn
## Try to restart up to 10 times within 5 min:
# respawn limit 10 300

The only necessary “stanzas” are “start on” and “exec” (see also Upstart for Java apps). We could also enable respawning (both the stanzas) so that Upstart would try to start the service again if it crashes. (Of course Clojure service never crash ;-) )

Logging

I use tools.logging with logback (ch.qos.logback/logback-classic) and its RollingFileAppender with a SizeAndTimeBasedFNATP to keep the logs to a reasonable size:

<configuration>
 
<appender name="FILE">
<file>/var/log/mongodiffer/mongodiffer.log</file>
<rollingPolicy>
<!-- rollover daily -->
<fileNamePattern>/var/log/mongodiffer/mongodiffer-%d{yyyy-MM}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy>
<!-- or whenever the file size reaches the size -->
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
 
<appender name="STDOUT">
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
 
<root level="info">
<appender-ref ref="FILE" />
<appender-ref ref="STDOUT" /> <!-- Useful when running locally/dev -->
</root>
</configuration>

Stdout and stderr of the service is automatically captured in /var/log/upstart/mongodiffer.log via the default console log stanza. I haven’t been able to find out what to do to make sure that it won’t grow infinitely.

Serving static resources from a Ring uberjar

  1. Put the resources under a directory in resources, f.ex. resources/static/ (=> e.g. resources/static/js/zepto/zepto.min.js)
  2. Configure the wrap-resource Ring middleware: “(wrap-resource “static”)”
  3. Refer to the resources using the path after static (e.g. “/js/zepto/zepto.min.js”)


Published at DZone with permission of Jakub Holý, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)