Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Update PocketChange to Lift 2.4 with Scala 2.9.1 #1

Merged
merged 3 commits into from

2 participants

@indrajitr

Further:

  • the application now used SBT 0.11.2
  • the dependencies have been updated to latest available ones and un-necessary ones have been culled out
indrajitr added some commits
@indrajitr indrajitr Migrate build to SBT (0.11.2)
Further, keep the build script minimal and remove unnecessary dependencies
3febd85
@indrajitr indrajitr Update to work with Lift 2.4 on Scala 2.9.1
While at this, cleanup Bootstrap and RestHelper usage a bit
e28f8cb
@indrajitr indrajitr Update Usage instruction
f722a6c
@ramnivas ramnivas merged commit 36ffb23 into from
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 9, 2012
  1. @indrajitr

    Migrate build to SBT (0.11.2)

    indrajitr authored
    Further, keep the build script minimal and remove unnecessary dependencies
  2. @indrajitr

    Update to work with Lift 2.4 on Scala 2.9.1

    indrajitr authored
    While at this, cleanup Bootstrap and RestHelper usage a bit
  3. @indrajitr

    Update Usage instruction

    indrajitr authored
This page is out of date. Refresh to see the latest.
View
17 PocketChange/build.sbt
@@ -0,0 +1,17 @@
+organization := "com.pocketchangeapp"
+
+name := "PocketChange"
+
+version := "0.3.1-SNAPSHOT"
+
+startYear := Some(2008)
+
+seq(webSettings :_*)
+
+scalaVersion := "2.9.1"
+
+libraryDependencies ++= Seq("net.liftweb" %% "lift-webkit" % "2.4",
+ "net.liftweb" %% "lift-mapper" % "2.4",
+ "jfree" % "jfreechart" % "1.0.13",
+ "com.h2database" % "h2" % "1.2.147" % "runtime",
+ "org.mortbay.jetty" % "jetty" % "6.1.26" % "container")
View
5 PocketChange/debug.sh
@@ -1,5 +0,0 @@
-#!/bin/bash
-
-export MAVEN_OPTS="-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=4000,server=y,suspend=y"
-
-mvn -Djetty.port=9090 jetty:run
View
202 PocketChange/pom.xml
@@ -1,202 +0,0 @@
-
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>com.pocketchangeapp</groupId>
- <artifactId>PocketChange</artifactId>
- <version>0.3</version>
- <packaging>war</packaging>
- <name>PocketChangeApp</name>
- <inceptionYear>2008</inceptionYear>
- <!--
- <description>A Lift Application to support the Lift Book.</description>
- -->
- <properties>
- <scala.version>2.8.1</scala.version>
- <lift.version>2.2</lift.version>
- </properties>
-
- <pluginRepositories>
- <pluginRepository>
- <id>scala-tools.org</id>
- <name>Scala Tools Maven2 Repository</name>
- <url>http://scala-tools.org/repo-releases</url>
- </pluginRepository>
- </pluginRepositories>
- <repositories>
- <repository>
- <id>scala-tools.org</id>
- <name>Scala Tools Maven2 Repository</name>
- <url>http://scala-tools.org/repo-releases</url>
- </repository>
- <repository>
- <id>scala-tools.org.snapshots</id>
- <name>Scala Tools Maven2 Repository</name>
- <url>http://scala-tools.org/repo-snapshots</url>
- <snapshots />
- </repository>
- </repositories>
-
- <developers>
- <developer>
- <id>dchenbecker</id>
- <name>Derek Chen-Becker</name>
- <timezone>0</timezone>
- </developer>
- <developer>
- <id>tyler.weir</id>
- <name>Tyler Weir</name>
- <timezone>-5</timezone>
- </developer>
- </developers>
-
- <dependencies>
- <dependency>
- <groupId>org.scala-lang</groupId>
- <artifactId>scala-library</artifactId>
- <version>${scala.version}</version>
- </dependency>
- <dependency>
- <groupId>net.liftweb</groupId>
- <artifactId>lift-webkit_${scala.version}</artifactId>
- <version>${lift.version}</version>
- </dependency>
- <dependency>
- <groupId>net.liftweb</groupId>
- <artifactId>lift-mapper_${scala.version}</artifactId>
- <version>${lift.version}</version>
- </dependency>
- <dependency>
- <groupId>jfree</groupId>
- <artifactId>jfreechart</artifactId>
- <version>1.0.11</version>
- <exclusions>
- <exclusion>
- <groupId>gnujaxp</groupId>
- <artifactId>gnujaxp</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>com.h2database</groupId>
- <artifactId>h2</artifactId>
- <version>1.1.112</version>
- </dependency>
- <dependency>
- <groupId>javax.servlet</groupId>
- <artifactId>servlet-api</artifactId>
- <version>2.5</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>3.8.1</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.mortbay.jetty</groupId>
- <artifactId>jetty</artifactId>
- <version>[6.1.12,)</version>
- <scope>test</scope>
- </dependency>
- <!--
- <dependency>
- <groupId>postgresql</groupId>
- <artifactId>postgresql</artifactId>
- <version>8.1-404.jdbc3</version>
- </dependency>
- -->
- <!-- for LiftConsole -->
- <dependency>
- <groupId>org.scala-lang</groupId>
- <artifactId>scala-compiler</artifactId>
- <version>${scala.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-classic</artifactId>
- <version>0.9.26</version>
- </dependency>
- </dependencies>
-
- <build>
- <sourceDirectory>src/main/scala</sourceDirectory>
- <testSourceDirectory>src/test/scala</testSourceDirectory>
- <plugins>
- <plugin>
- <groupId>org.scala-tools</groupId>
- <artifactId>maven-scala-plugin</artifactId>
- <executions>
- <execution>
- <goals>
- <goal>compile</goal>
- <goal>testCompile</goal>
- </goals>
- </execution>
- </executions>
- <configuration>
- <scalaVersion>${scala.version}</scalaVersion>
- <args>
- <!-- arg>-unchecked</arg -->
- <arg>-deprecation</arg>
- </args>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.mortbay.jetty</groupId>
- <artifactId>maven-jetty-plugin</artifactId>
- <configuration>
- <contextPath>/</contextPath>
- <scanIntervalSeconds>5</scanIntervalSeconds>
- </configuration>
- </plugin>
- <plugin>
- <groupId>net.sf.alchim</groupId>
- <artifactId>yuicompressor-maven-plugin</artifactId>
- <executions>
- <execution>
- <goals>
- <goal>compress</goal>
- </goals>
- </execution>
- </executions>
- <configuration>
- <nosuffix>true</nosuffix>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-eclipse-plugin</artifactId>
- <configuration>
- <downloadSources>true</downloadSources>
- <excludes>
- <exclude>org.scala-lang:scala-library</exclude>
- </excludes>
- <classpathContainers>
- <classpathContainer>ch.epfl.lamp.sdt.launching.SCALA_CONTAINER</classpathContainer>
- </classpathContainers>
- <projectnatures>
- <java.lang.String>ch.epfl.lamp.sdt.core.scalanature</java.lang.String>
- <java.lang.String>org.eclipse.jdt.core.javanature</java.lang.String>
- </projectnatures>
- <buildcommands>
- <java.lang.String>ch.epfl.lamp.sdt.core.scalabuilder</java.lang.String>
- </buildcommands>
- </configuration>
- </plugin>
- </plugins>
- </build>
- <reporting>
- <plugins>
- <plugin>
- <groupId>org.scala-tools</groupId>
- <artifactId>maven-scala-plugin</artifactId>
- <configuration>
- <scalaVersion>${scala.version}</scalaVersion>
- </configuration>
- </plugin>
- </plugins>
- </reporting>
-</project>
-
View
1  PocketChange/project/build.properties
@@ -0,0 +1 @@
+sbt.version=0.11.2
View
1  PocketChange/project/plugins.sbt
@@ -0,0 +1 @@
+libraryDependencies <+= (sbtVersion in update)(v => "com.github.siasia" %% "xsbt-web-plugin" % (v + "-0.2.11"))
View
128 PocketChange/src/main/scala/bootstrap/liftweb/Boot.scala
@@ -1,28 +1,26 @@
package bootstrap.liftweb
-import java.sql.{Connection, DriverManager}
-
-import net.liftweb.common.{Box,Empty,Failure,Full,Logger}
-import net.liftweb.util.{Helpers,LoanWrapper,Props}
-import net.liftweb.http.{GetRequest,LiftRules,ParsePath,PutRequest,Req,
- RequestVar,RewriteRequest,RewriteResponse,S}
-import org.slf4j.MDC
-import com.pocketchangeapp.snippet.{AddEntry, Accounts}
-import net.liftweb.sitemap.{Loc, Menu, SiteMap}
-import net.liftweb.mapper._
+import net.liftweb._
+import common.{MDC, Full, Logger}
+import util.Props
+import http.{LiftRules, ParsePath, Req, RewriteRequest, RewriteResponse, S}
+import sitemap.{Loc, Menu, SiteMap}
+import mapper._
// Get implicit conversions
-import net.liftweb.sitemap.Loc._
-import net.liftweb.util.Helpers._
+import util.Helpers._
+
+import com.pocketchangeapp.snippet.{AddEntry, Accounts}
import com.pocketchangeapp.model._
import com.pocketchangeapp.api._
-import com.pocketchangeapp.util.{Charting,Image}
+import com.pocketchangeapp.util.{Charting, Image}
+
/**
* The bootstrap.liftweb.Boot class is the main entry point for Lift.
* This is where all of the initial App setup is performed. In particular,
* the Boot.boot method is what lift calls after it loads.
- *
+ *
* TODO: Connect Lucene/Compass for search
*/
class Boot {
@@ -46,20 +44,13 @@ class Boot {
*/
LiftRules.addToPackages("com.pocketchangeapp")
-
if (!DB.jndiJdbcConnAvailable_?) DB.defineConnectionManager(DefaultConnectionIdentifier, DBVendor)
- // This method is here due to type conflicts when attempting to use
- // a bare method.
- def schemeLogger (msg : => AnyRef) = {
- logger.info(msg)
- }
-
- Schemifier.schemify(true, schemeLogger _,
- User, Tag, Account, AccountAdmin,
- AccountViewer, AccountNote, Expense, ExpenseTag)
+ Schemifier.schemify(true, Schemifier.infoF _,
+ User, Tag, Account, AccountAdmin,
+ AccountViewer, AccountNote, Expense, ExpenseTag)
- LiftRules.setSiteMap(SiteMap(MenuInfo.menu :_*))
+ LiftRules.setSiteMap(SiteMap(MenuInfo.menu: _*))
// Tie in the REST API. Uncomment the one you want to use
//LiftRules.dispatch.prepend(DispatchRestAPI.dispatch)
@@ -74,21 +65,21 @@ class Boot {
// Set up some rewrites
LiftRules.statelessRewrite.append {
case RewriteRequest(ParsePath(List("account", acctName), _, _, _), _, _) =>
- RewriteResponse("viewAcct" :: Nil, Map("name" -> urlDecode(acctName)))
+ RewriteResponse("viewAcct" :: Nil, Map("name" -> urlDecode(acctName)))
case RewriteRequest(ParsePath(List("account", acctName, tag), _, _, _), _, _) =>
- RewriteResponse("viewAcct" :: Nil, Map("name" -> urlDecode(acctName), "tag" -> urlDecode(tag)))
+ RewriteResponse("viewAcct" :: Nil, Map("name" -> urlDecode(acctName), "tag" -> urlDecode(tag)))
}
// Custom dispatch for graph and receipt image generation
LiftRules.dispatch.append {
case Req(List("graph", acctName, "history"), _, _) =>
- () => Charting.history(acctName)
+ () => Charting.history(acctName)
case Req(List("graph", acctName, "tagpie"), _, _) =>
- () => Charting.tagpie(acctName)
+ () => Charting.tagpie(acctName)
case Req(List("graph", acctName, "tagbar"), _, _) =>
- () => Charting.tagbar(acctName)
+ () => Charting.tagbar(acctName)
case Req(List("image", expenseId), _, _) =>
- () => Full(Image.viewImage(expenseId))
+ () => Full(Image.viewImage(expenseId))
}
// Hook in our REST API auth
@@ -96,40 +87,41 @@ class Boot {
/* We're going to use HTTP Basic auth for REST, although
* technically this allows for its use anywhere in the app. */
- import net.liftweb.http.auth.{AuthRole,HttpBasicAuthentication,userRoles}
+ import net.liftweb.http.auth.{AuthRole, HttpBasicAuthentication, userRoles}
LiftRules.authentication = HttpBasicAuthentication("PocketChange") {
case (userEmail, userPass, _) => {
logger.debug("Authenticating: " + userEmail)
- User.find(By(User.email, userEmail)).map { user =>
- if (user.password.match_?(userPass)) {
- logger.info("Auth succeeded for " + userEmail)
- User.logUserIn(user)
-
- // Set an MDC for logging purposes
- MDC.put("user", user.shortName)
-
- // Compute all of the user roles
- userRoles(user.editable.map(acct => AuthRole("editAcct:" + acct.id)) ++
- user.allAccounts.map(acct => AuthRole("viewAcct:" + acct.id)))
- true
- } else {
- logger.warn("Auth failed for " + userEmail)
- false
- }
+ User.find(By(User.email, userEmail)).map {
+ user =>
+ if (user.password.match_?(userPass)) {
+ logger.info("Auth succeeded for " + userEmail)
+ User.logUserIn(user)
+
+ // Set an MDC for logging purposes
+ MDC.put("user" -> user.shortName)
+
+ // Compute all of the user roles
+ userRoles(user.editable.map(acct => AuthRole("editAcct:" + acct.id)) ++
+ user.allAccounts.map(acct => AuthRole("viewAcct:" + acct.id)))
+ true
+ } else {
+ logger.warn("Auth failed for " + userEmail)
+ false
+ }
} openOr false
}
}
-// // Add a query logger (directly)
-// DB.addLogFunc {
-// case (log, duration) => {
-// logger.debug("Total query time : %d ms".format(duration))
-// log.allEntries.foreach {
-// case DBLogEntry(stmt,duration) =>
-// logger.debug(" %s in %d ms".format(stmt, duration))
-// }
-// }
-// }
+ // // Add a query logger (directly)
+ // DB.addLogFunc {
+ // case (log, duration) => {
+ // logger.debug("Total query time : %d ms".format(duration))
+ // log.allEntries.foreach {
+ // case DBLogEntry(stmt,duration) =>
+ // logger.debug(" %s in %d ms".format(stmt, duration))
+ // }
+ // }
+ // }
// Add a query logger (via S.queryLog)
DB.addLogFunc(DB.queryCollector)
@@ -138,8 +130,8 @@ class Boot {
case (Full(req), duration, log) => {
logger.debug(("Total request time on %s: %d ms").format(req.uri, duration))
log.foreach {
- case (stmt,duration) =>
- logger.debug(" %s in %d ms".format(stmt, duration))
+ case (stmt, dur) =>
+ logger.debug(" %s in %d ms".format(stmt, dur))
}
}
case _ => // we don't log for non-requests
@@ -150,20 +142,20 @@ class Boot {
}
object MenuInfo {
+
import Loc._
- import net.liftweb.sitemap.**
// Define a simple test clause that we can use for multiple menu items
val IfLoggedIn = If(() => User.currentUser.isDefined, "You must be logged in")
- def menu: List[Menu] =
+ def menu: List[Menu] =
List[Menu](Menu.i("Home") / "index",
- Menu.i("Manage Accounts") / "manage" >> IfLoggedIn,
- Menu.i("Add Account") / "editAcct" >> Hidden >> IfLoggedIn,
- Menu.i("View Account") / "viewAcct" / ** >> Hidden >> IfLoggedIn,
- Menu.i("Help") / "help" / "index") :::
- User.sitemap
-
+ Menu.i("Manage Accounts") / "manage" >> IfLoggedIn,
+ Menu.i("Add Account") / "editAcct" >> Hidden >> IfLoggedIn,
+ Menu.i("View Account") / "viewAcct" / sitemap.** >> Hidden >> IfLoggedIn,
+ Menu.i("Help") / "help" / "index") :::
+ User.sitemap
+
}
object DBVendor extends StandardDBVendor(
View
63 PocketChange/src/main/scala/com/pocketchangeapp/api/RestAPI.scala
@@ -2,7 +2,7 @@
* RestAPI.scala
*
* Copyright 2008-2010 Derek Chen-Becker, Marius Danciu and Tyler Wier
- *
+ *
*/
package com.pocketchangeapp {
package api {
@@ -21,13 +21,14 @@ import net.liftweb.http.js.JE.JsRaw
import net.liftweb.mapper.By
import model._
+import net.liftweb.json.JsonAST.JValue
/**
* This object provides a REST API for Pocketchange utilizing the
* older-style XMLApiHelper. For the newer style, see RestHelperAPI.
*/
object DispatchRestAPI extends XMLApiHelper {
- final val logger = Logger("com.pocketchangeapp.api.DispatchRestAPI")
+ final val logger = Logger(getClass)
// Import our methods for converting things around
import RestFormatters._
@@ -36,31 +37,31 @@ object DispatchRestAPI extends XMLApiHelper {
* This method provides the dispatch hooks for our REST API that
* we'll hook into LiftRules.dispatch
*/
- def dispatch: LiftRules.DispatchPF = {
+ def dispatch: LiftRules.DispatchPF = {
// Define our getters first
- case Req(List("api", "expense", Expense(expense,_)), _, GetRequest) =>
+ case Req(List("api", "expense", Expense(expense,_)), _, GetRequest) =>
() => Full(toXML(expense)) // default to XML
- case Req(List("api", "expense", Expense(expense,_), "xml"), _, GetRequest) =>
+ case Req(List("api", "expense", Expense(expense,_), "xml"), _, GetRequest) =>
() => Full(toXML(expense))
- case Req(List("api", "expense", Expense(expense,_), "json"), _, GetRequest) =>
+ case Req(List("api", "expense", Expense(expense,_), "json"), _, GetRequest) =>
() => JsonResponse(toJSON(expense))
case Req(List("api", "account", Account(account)), _, GetRequest) =>
() => AtomResponse(toAtom(account))
// Define the PUT handler for both XML and JSON MIME types
- case request @ Req(List("api", "account", Account(account)), _, PutRequest)
- if request.xml_? =>
- () => addExpense(fromXML(request.xml,account),
- account,
+ case request @ Req(List("api", "account", Account(account)), _, PutRequest)
+ if request.xml_? =>
+ () => addExpense(fromXML(request.xml,account),
+ account,
result => CreatedResponse(toXML(result), "text/xml"))
- case request @ Req(List("api", "account", Account(account)), _, PutRequest)
- if request.json_? =>
- () => addExpense(fromJSON(request.body,account),
+ case request @ Req(List("api", "account", Account(account)), _, PutRequest)
+ if request.json_? =>
+ () => addExpense(fromJSON(request.body,account),
account,
result => JsonResponse(toJSON(result), Nil, Nil, 201))
// Invalid API request - route to our error handler
- case Req("api" :: x :: Nil, "", _) =>
+ case Req("api" :: x :: Nil, "", _) =>
() => BadResponse() // Everything else fails
}
@@ -71,9 +72,9 @@ object DispatchRestAPI extends XMLApiHelper {
*/
import net.liftweb.http.auth.AuthRole
def protection : LiftRules.HttpAuthProtectedResourcePF = {
- case Req(List("api", "account", accountId), _, PutRequest) =>
+ case Req(List("api", "account", accountId), _, PutRequest) =>
Full(AuthRole("editAcct:" + accountId))
- case Req(List("api", "account", accountId), _, GetRequest) =>
+ case Req(List("api", "account", accountId), _, GetRequest) =>
Full(AuthRole("viewAcct:" + accountId))
// If the account is public, don't enforce auth
case Req(List("api", "expense", Expense(e, true)), _, GetRequest) => Empty
@@ -82,28 +83,28 @@ object DispatchRestAPI extends XMLApiHelper {
}
// We're overriding this because we don't want the operation and success tags
- override def buildResponse (success: Boolean,
+ override def buildResponse (success: Boolean,
msg : Box[NodeSeq],
body : NodeSeq) = XmlResponse(body.head)
def createTag(in : NodeSeq) : Elem = <unused/>
// reacts to the PUT Request
- def addExpense(parsedExpense : Box[Expense],
- account : Account,
+ def addExpense(parsedExpense : Box[Expense],
+ account : Account,
success : Expense => LiftResponse): LiftResponse = parsedExpense match {
case Full(expense) => {
- val (entrySerial,entryBalance) =
+ val (entrySerial,entryBalance) =
Expense.getLastExpenseData(account, expense.dateOf)
expense.account(account).serialNumber(entrySerial + 1).
currentBalance(entryBalance + expense.amount)
-
+
expense.validate match {
case Nil => {
Expense.updateEntries(entrySerial + 1, expense.amount.is)
expense.save
-
+
account.balance(account.balance.is + expense.amount.is).save
success(expense)
@@ -133,7 +134,7 @@ object RestHelperAPI extends RestHelper {
// Service Atom and requests that don't request a specific format
serve {
// Default to XML
- case Get(List("api", "expense", Expense(expense,_)), _) =>
+ case Get(List("api", "expense", Expense(expense,_)), _) =>
() => Full(toXML(expense))
case Get(List("api", "account", Account(account)), _) =>
() => Full(AtomResponse(toAtom(account)))
@@ -144,7 +145,7 @@ object RestHelperAPI extends RestHelper {
serve {
case XmlPut(List("api", "account", Account(account)), (body, request)) =>
() => Full(addExpense(fromXML(Full(body),account),
- account,
+ account,
result => CreatedResponse(toXML(result), "text/xml")))
case JsonPut(List("api", "account", Account(account)), (_, request)) =>
() => Full(addExpense(fromJSON(request.body,account),
@@ -152,26 +153,26 @@ object RestHelperAPI extends RestHelper {
result => JsonResponse(toJSON(result), Nil, Nil, 201)))
}
-
+
// Define an implicit conversion from an Expense to XML or JSON
import net.liftweb.http.rest.{JsonSelect,XmlSelect}
- implicit def expenseToRestResponse : JxCvtPF[Expense] = {
+ implicit def expenseToRestResponse: JxCvtPF[Expense] = {
case (JsonSelect, e, _) => toJSON(e)
case (XmlSelect, e, _) => toXML(e)
}
-
- serveJx {
+
+ serveJx[Expense] {
case Get(List("api", "expense", Expense(expense,_)), _) => Full(expense)
}
// Just an example of autoconversion
- serveJx {
- case Get(List("api", "greet", name),_) =>
+ serveJx[AutoJsonXmlAble] {
+ case Get(List("api", "greet", name),_) =>
auto(Map("greeting" ->
Map("who" -> name,
"what" -> ("Hello at " + new java.util.Date))))
}
-
+
}
// Close package statements
View
16 README.md
@@ -1,13 +1,25 @@
This fork externalizes database properties so that the unmodified application can run in <a href="http://cloudfoundry.org">Cloud Foundry</a>
+### About
+
This is the demo application that we're building to support the Lift Book.
[Lift Book repo](http://www.github.com/tjweir/liftbook/)
-[LiftWeb Source](http://www.github.com/lift/lift)
-
+[LiftWeb Source](http://www.github.com/lift/framework)
- Derek, Marius and Tyler
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/"><img alt="Creative Commons License" style="border-width:0" src="http://i.creativecommons.org/l/by-sa/3.0/88x31.png" /></a><br /><span xmlns:dct="http://purl.org/dc/terms/" property="dct:title">PocketChange</span> is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/">Creative Commons Attribution-ShareAlike 3.0 Unported License</a>.
+### Usage
+
+To run the application, you'll need [SBT 0.11.2](https://github.com/harrah/xsbt/wiki/Getting-Started-Setup).
+
+Once, you have SBT set up, to start the application with [Jety on port 8080](http://localhost:8080) use this in console:
+
+ sbt container:start
+
+To stop the application use this in console:
+
+ sbt container:stop
Something went wrong with that request. Please try again.