Scalatra, Scalate and Scaml
A while ago I set about creating a webapp for monitoring the status of the various Quartz jobs that we use to keep our main application ticking. It was put together quickly using Simple Build Tool and Scalatra. I needed a templating engine and noticed that Scalatra has support for Scalate (although at the time the support was only experimental). I decided to give it a try. The first step was to add the dependencies for Scalate and Scalatra-Scalate into our build file:
val scalatraScalate = "org.scalatra" % "scalatra-scalate_2.8.0" % "2.0.0.M1" val scalateCore = "org.fusesource.scalate" % "scalate-core" % "1.2"
I soon ran into the Scalatra issue with using Scalate and Logback and instead decided to use Scalate on its own. Referring to the Scalate Embedding Guide, I created an instance of the Scalate TemplateEngine class in a trait that is extended by my Scalatra servlet:
trait ScalateTemplateEngine {
def render(templatePath: String, context: Map[String, Any], response: HttpServletResponse) = {
val templateEngine = new TemplateEngine
val template = templateEngine.load(templatePath)
val buffer = new StringWriter()
val renderContext = new DefaultRenderContext(templateEngine, new PrintWriter(buffer))
context.foreach({case (key, value) => renderContext.attributes(key) = value})
template.render(renderContext)
response.getWriter.write(buffer.toString)
}
}
Then, referring to this excellent blog post, I created an index.scaml file. The scaml file (below) is concise and reasonably readable. The only hiccups I encountered in making it were:
- Figuring out the syntax and correct indenting of the for loop
- Realising that all the values the map are automatically converted to Options (hence the many get calls).
-@ val title: String = "Scheduler"
-@ val header: String = "Scheduler"
-@ val quartzStatus: String = "No quartz status information available"
-@ val scheduledTasks: List[Map[String, String]] = Nil
-@ val quartzInformation: String = "No quartz information available"
!!! 5
%html
%head
%title= title
%body
%h1
!= header
%h5
!= quartzStatus
%table{:border => 1}
%thead
%td Task
%td Status
%td Quantity
%td Last Attempt Start Time
%td Last Attempt Finish Time
%td Last Successful Run Start Time
%td Last Successful Run Finish Time
%td Last Error
%td Action
= for(scheduledTask <- scheduledTasks)
%tbody
%td= scheduledTask.get("name").get
%td= scheduledTask.get("status").get
%td= scheduledTask.get("quantity").get
%td= scheduledTask.get("lastRunStart").get
%td= scheduledTask.get("lastRunFinish").get
%td= scheduledTask.get("lastSuccessStart").get
%td= scheduledTask.get("lastSuccessFinish").get
%td= scheduledTask.get("lastException").get
%td
%form(method="post")
%input(type="submit" name={scheduledTask.get("action").get} value="Run now")
note that you can use the Map.apply() method in Scala to avoid using the get() method which returns an Option. So
scheduledTask.get(“lastRunFinish”).get
can be
scheduledTask(“lastRunFinish”)
jastrachan
December 15, 2010 at 12:26 pm