Removing playMini as to avoid the cyclic dependency, please look at the play-mini docs for examples
This commit is contained in:
parent
bf49681171
commit
572382b220
4 changed files with 3 additions and 319 deletions
|
|
@ -1,6 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
|
||||||
*/
|
|
||||||
//#global
|
|
||||||
object Global extends com.typesafe.play.mini.Setup(akka.docs.http.PlayMiniApplication)
|
|
||||||
//#global
|
|
||||||
|
|
@ -1,128 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
|
||||||
*/
|
|
||||||
package akka.docs.http
|
|
||||||
|
|
||||||
//#imports
|
|
||||||
import com.typesafe.play.mini.{ POST, GET, Path, Application }
|
|
||||||
import play.api.mvc.{ Action, AsyncResult }
|
|
||||||
import play.api.mvc.Results._
|
|
||||||
import play.api.libs.concurrent._
|
|
||||||
import play.api.data._
|
|
||||||
import play.api.data.Forms._
|
|
||||||
import akka.pattern.ask
|
|
||||||
import akka.util.Timeout
|
|
||||||
import akka.util.duration._
|
|
||||||
import akka.actor.{ ActorSystem, Props, Actor }
|
|
||||||
import scala.collection.mutable.{ Map ⇒ MutableMap }
|
|
||||||
//#imports
|
|
||||||
|
|
||||||
//#playMiniDefinition
|
|
||||||
object PlayMiniApplication extends Application {
|
|
||||||
//#playMiniDefinition
|
|
||||||
private val system = ActorSystem("sample")
|
|
||||||
//#regexURI
|
|
||||||
private final val StatementPattern = """/account/statement/(\w+)""".r
|
|
||||||
//#regexURI
|
|
||||||
private lazy val accountActor = system.actorOf(Props[AccountActor])
|
|
||||||
implicit val timeout = Timeout(1000 milliseconds)
|
|
||||||
|
|
||||||
//#route
|
|
||||||
def route = {
|
|
||||||
//#routeLogic
|
|
||||||
//#simpleGET
|
|
||||||
case GET(Path("/ping")) ⇒ Action {
|
|
||||||
Ok("Pong @ " + System.currentTimeMillis)
|
|
||||||
}
|
|
||||||
//#simpleGET
|
|
||||||
//#regexGET
|
|
||||||
case GET(Path(StatementPattern(accountId))) ⇒ Action {
|
|
||||||
AsyncResult {
|
|
||||||
//#innerRegexGET
|
|
||||||
(accountActor ask Status(accountId)).mapTo[Int].asPromise.map { r ⇒
|
|
||||||
if (r >= 0) Ok("Account total: " + r)
|
|
||||||
else BadRequest("Unknown account: " + accountId)
|
|
||||||
}
|
|
||||||
//#innerRegexGET
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//#regexGET
|
|
||||||
//#asyncDepositPOST
|
|
||||||
case POST(Path("/account/deposit")) ⇒ Action { implicit request ⇒
|
|
||||||
//#formAsyncDepositPOST
|
|
||||||
val (accountId, amount) = commonForm.bindFromRequest.get
|
|
||||||
//#formAsyncDepositPOST
|
|
||||||
AsyncResult {
|
|
||||||
(accountActor ask Deposit(accountId, amount)).mapTo[Int].asPromise.map { r ⇒ Ok("Updated account total: " + r) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//#asyncDepositPOST
|
|
||||||
//#asyncWithdrawPOST
|
|
||||||
case POST(Path("/account/withdraw")) ⇒ Action { implicit request ⇒
|
|
||||||
val (accountId, amount) = commonForm.bindFromRequest.get
|
|
||||||
AsyncResult {
|
|
||||||
(accountActor ask Withdraw(accountId, amount)).mapTo[Int].asPromise.map { r ⇒
|
|
||||||
if (r >= 0) Ok("Updated account total: " + r)
|
|
||||||
else BadRequest("Unknown account or insufficient funds. Get your act together.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//#asyncWithdrawPOST
|
|
||||||
//#routeLogic
|
|
||||||
}
|
|
||||||
//#route
|
|
||||||
|
|
||||||
//#form
|
|
||||||
val commonForm = Form(
|
|
||||||
tuple(
|
|
||||||
"accountId" -> nonEmptyText,
|
|
||||||
"amount" -> number(min = 1)))
|
|
||||||
//#form
|
|
||||||
}
|
|
||||||
|
|
||||||
//#cases
|
|
||||||
case class Status(accountId: String)
|
|
||||||
case class Deposit(accountId: String, amount: Int)
|
|
||||||
case class Withdraw(accountId: String, amount: Int)
|
|
||||||
//#cases
|
|
||||||
|
|
||||||
//#actor
|
|
||||||
class AccountActor extends Actor {
|
|
||||||
var accounts = MutableMap[String, Int]()
|
|
||||||
|
|
||||||
//#receive
|
|
||||||
def receive = {
|
|
||||||
//#senderBang
|
|
||||||
case Status(accountId) ⇒ sender ! accounts.getOrElse(accountId, -1)
|
|
||||||
//#senderBang
|
|
||||||
case Deposit(accountId, amount) ⇒ sender ! deposit(accountId, amount)
|
|
||||||
case Withdraw(accountId, amount) ⇒ sender ! withdraw(accountId, amount)
|
|
||||||
}
|
|
||||||
//#receive
|
|
||||||
|
|
||||||
private def deposit(accountId: String, amount: Int): Int = {
|
|
||||||
accounts.get(accountId) match {
|
|
||||||
case Some(value) ⇒
|
|
||||||
val newValue = value + amount
|
|
||||||
accounts += accountId -> newValue
|
|
||||||
newValue
|
|
||||||
case None ⇒
|
|
||||||
accounts += accountId -> amount
|
|
||||||
amount
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private def withdraw(accountId: String, amount: Int): Int = {
|
|
||||||
accounts.get(accountId) match {
|
|
||||||
case Some(value) ⇒
|
|
||||||
if (value < amount) -1
|
|
||||||
else {
|
|
||||||
val newValue = value - amount
|
|
||||||
accounts += accountId -> newValue
|
|
||||||
newValue
|
|
||||||
}
|
|
||||||
case None ⇒ -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//#actor
|
|
||||||
}
|
|
||||||
|
|
@ -19,182 +19,4 @@ Getting started
|
||||||
First you must make your application aware of play-mini.
|
First you must make your application aware of play-mini.
|
||||||
In SBT you just have to add the following to your _libraryDependencies_::
|
In SBT you just have to add the following to your _libraryDependencies_::
|
||||||
|
|
||||||
libraryDependencies += "com.typesafe" %% "play-mini" % "2.0-RC3-SNAPSHOT"
|
libraryDependencies += "com.typesafe" %% "play-mini" % "<version-number>"
|
||||||
|
|
||||||
Sample Application
|
|
||||||
------------------
|
|
||||||
|
|
||||||
To illustrate how easy it is to wire a RESTful service with Akka we will use a sample application.
|
|
||||||
The aim of the application is to show how to use play-mini and Akka in combination. Do not put too much
|
|
||||||
attention on the actual business logic itself, which is a extremely simple bank application, as building a bank
|
|
||||||
application is a little more complex than what's shown in the sample...
|
|
||||||
|
|
||||||
The application should support the following URL commands:
|
|
||||||
- GET /ping - returns a Pong message with the time of the server (used to see if the application is up and running)
|
|
||||||
- GET /account/statement/{accountId} - returns the account statement
|
|
||||||
- POST /account/deposit - deposits money to an account (and creates a new one if it's not already existing)
|
|
||||||
- POST /account/withdraw - withdraws money from an account
|
|
||||||
|
|
||||||
Error messages will be returned in case of any misuse of the application, e.g. withdrawing more money than an
|
|
||||||
account has etc.
|
|
||||||
|
|
||||||
Getting started
|
|
||||||
---------------
|
|
||||||
|
|
||||||
To build a play-mini application you first have to make your object extend com.typesafe.play.mini.Application:
|
|
||||||
|
|
||||||
.. includecode:: code/akka/docs/http/PlayMiniApplication.scala
|
|
||||||
:include: playMiniDefinition
|
|
||||||
|
|
||||||
The next step is to implement the mandatory method ``route``:
|
|
||||||
|
|
||||||
.. includecode:: code/akka/docs/http/PlayMiniApplication.scala
|
|
||||||
:include: route
|
|
||||||
:exclude: routeLogic
|
|
||||||
|
|
||||||
It is inside the ``route`` method that all the magic happens.
|
|
||||||
In the sections below we will show how to set up play-mini to handle both GET and POST HTTP calls.
|
|
||||||
|
|
||||||
Simple GET
|
|
||||||
----------
|
|
||||||
|
|
||||||
We start off by creating the simplest method we can - a "ping" method:
|
|
||||||
|
|
||||||
.. includecode:: code/akka/docs/http/PlayMiniApplication.scala
|
|
||||||
:include: simpleGET
|
|
||||||
|
|
||||||
As you can see in the section above play-mini uses Scala's wonderful pattern matching.
|
|
||||||
In the snippet we instruct play-mini to reply to all HTTP GET calls with the URI "/ping".
|
|
||||||
The ``Action`` returned comes from Play! and you can find more information about it `here <https://github.com/playframework/Play20/wiki/ScalaActions>`_.
|
|
||||||
|
|
||||||
.. _Advanced-GET:
|
|
||||||
|
|
||||||
Advanced GET
|
|
||||||
------------
|
|
||||||
|
|
||||||
Let's try something more advanced, retrieving parameters from the URI and also make an asynchronous call to an actor:
|
|
||||||
|
|
||||||
.. includecode:: code/akka/docs/http/PlayMiniApplication.scala
|
|
||||||
:include: regexGET
|
|
||||||
|
|
||||||
The regular expression looks like this:
|
|
||||||
|
|
||||||
.. includecode:: code/akka/docs/http/PlayMiniApplication.scala
|
|
||||||
:include: regexURI
|
|
||||||
|
|
||||||
In the snippets above we extract a URI parameter with the help of a simple regular expression and then we pass this
|
|
||||||
parameter on to the underlying actor system. As you can see ``AsyncResult`` is being used. This means that the call to
|
|
||||||
the actor will be performed asynchronously, i.e. no blocking.
|
|
||||||
|
|
||||||
The asynchronous call to the actor is being done with a ``ask``, e.g.::
|
|
||||||
|
|
||||||
(accountActor ask Status(accountId))
|
|
||||||
|
|
||||||
The actor that receives the message returns the result by using a standard *sender !*
|
|
||||||
as can be seen here:
|
|
||||||
|
|
||||||
.. includecode:: code/akka/docs/http/PlayMiniApplication.scala
|
|
||||||
:include: senderBang
|
|
||||||
|
|
||||||
When the result is returned to the calling code we use some mapping code in Play to convert a Akka future to a Play future.
|
|
||||||
This is shown in this code:
|
|
||||||
|
|
||||||
.. includecode:: code/akka/docs/http/PlayMiniApplication.scala
|
|
||||||
:include: innerRegexGET
|
|
||||||
|
|
||||||
In this snippet we check the result to decide what type of response we want to send to the calling client.
|
|
||||||
|
|
||||||
Using HTTP POST
|
|
||||||
---------------
|
|
||||||
|
|
||||||
Okay, in the sections above we have shown you how to use play-mini for HTTP GET calls. Let's move on to when the user
|
|
||||||
posts values to the application.
|
|
||||||
|
|
||||||
.. includecode:: code/akka/docs/http/PlayMiniApplication.scala
|
|
||||||
:include: asyncDepositPOST
|
|
||||||
|
|
||||||
As you can see the structure is almost the same as for the :ref:`Advanced-GET`. The difference is that we make the
|
|
||||||
``request`` parameter ``implicit`` and also that the following line of code is used to extract parameters from the POST.
|
|
||||||
|
|
||||||
.. includecode:: code/akka/docs/http/PlayMiniApplication.scala
|
|
||||||
:include: formAsyncDepositPOST
|
|
||||||
|
|
||||||
The code snippet used to map the call to parameters looks like this:
|
|
||||||
|
|
||||||
.. includecode:: code/akka/docs/http/PlayMiniApplication.scala
|
|
||||||
:include: form
|
|
||||||
|
|
||||||
Apart from the mapping of parameters the call to the actor looks is done the same as in :ref:`Advanced-GET`.
|
|
||||||
|
|
||||||
The Complete Code Sample
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
Below is the complete application in all its beauty.
|
|
||||||
|
|
||||||
Global.scala (<yourApp>/src/main/scala/Global.scala):
|
|
||||||
|
|
||||||
.. includecode:: code/Global.scala
|
|
||||||
|
|
||||||
PlayMiniApplication.scala (<yourApp>/src/main/scala/akka/docs/http/PlayMiniApplication.scala):
|
|
||||||
|
|
||||||
.. includecode:: code/akka/docs/http/PlayMiniApplication.scala
|
|
||||||
|
|
||||||
Build.scala (<yourApp>/project/Build.scala):
|
|
||||||
|
|
||||||
.. code-block:: scala
|
|
||||||
|
|
||||||
import sbt._
|
|
||||||
import Keys._
|
|
||||||
|
|
||||||
object PlayMiniApplicationBuild extends Build {
|
|
||||||
lazy val root = Project(id = "play-mini-application", base = file("."), settings = Project.defaultSettings).settings(
|
|
||||||
libraryDependencies += "com.typesafe" %% "play-mini" % "2.0-RC3-SNAPSHOT",
|
|
||||||
mainClass in (Compile, run) := Some("play.core.server.NettyServer"))
|
|
||||||
}
|
|
||||||
|
|
||||||
Running the Application
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
Firstly, start up the application by opening a command terminal and type::
|
|
||||||
|
|
||||||
> sbt
|
|
||||||
> run
|
|
||||||
|
|
||||||
Now you should see something similar to this in your terminal window::
|
|
||||||
|
|
||||||
[info] Running play.core.server.NettyServer
|
|
||||||
Play server process ID is 2523
|
|
||||||
[info] play - Application started (Prod)
|
|
||||||
[info] play - Listening for HTTP on port 9000...
|
|
||||||
|
|
||||||
In this example we will use the awesome `cURL <http://en.wikipedia.org/wiki/CURL>`_ command to interact with the application.
|
|
||||||
Fire up a command terminal and try the application out::
|
|
||||||
|
|
||||||
First we check the status of a couple of accounts:
|
|
||||||
> curl http://localhost:9000/account/statement/TheDudesAccount
|
|
||||||
Unknown account: TheDudesAccount
|
|
||||||
> curl http://localhost:9000/account/statement/MrLebowskisAccount
|
|
||||||
Unknown account: MrLebowskisAccount
|
|
||||||
|
|
||||||
Now deposit some money to the accounts:
|
|
||||||
> curl -d "accountId=TheDudesAccount&amount=1000" http://localhost:9000/account/deposit
|
|
||||||
Updated account total: 1000
|
|
||||||
> curl -d "accountId=MrLebowskisAccount&amount=500" http://localhost:9000/account/deposit
|
|
||||||
Updated account total: 500
|
|
||||||
|
|
||||||
Next thing is to check the status of the account:
|
|
||||||
> curl http://localhost:9000/account/statement/TheDudesAccount
|
|
||||||
Account total: 1000
|
|
||||||
> curl http://localhost:9000/account/statement/MrLebowskisAccount
|
|
||||||
Account total: 500
|
|
||||||
|
|
||||||
Fair enough, let's try to withdraw some cash shall we:
|
|
||||||
> curl -d "accountId=TheDudesAccount&amount=999" http://localhost:9000/account/withdraw
|
|
||||||
Updated account total: 1
|
|
||||||
> curl -d "accountId=MrLebowskisAccount&amount=999" http://localhost:9000/account/withdraw
|
|
||||||
Unknown account or insufficient funds. Get your act together.
|
|
||||||
> curl -d "accountId=MrLebowskisAccount&amount=500" http://localhost:9000/account/withdraw
|
|
||||||
Updated account total: 0
|
|
||||||
|
|
||||||
Yeah, it works!
|
|
||||||
Now we leave it to the astute reader of this document to take advantage of the power of play-mini and Akka.
|
|
||||||
|
|
@ -470,7 +470,7 @@ object Dependencies {
|
||||||
|
|
||||||
val tutorials = Seq(Test.scalatest, Test.junit)
|
val tutorials = Seq(Test.scalatest, Test.junit)
|
||||||
|
|
||||||
val docs = Seq(Test.scalatest, Test.junit, playMini)
|
val docs = Seq(Test.scalatest, Test.junit)
|
||||||
|
|
||||||
val zeroMQ = Seq(Test.scalatest, Test.junit, protobuf, Dependency.zeroMQ)
|
val zeroMQ = Seq(Test.scalatest, Test.junit, protobuf, Dependency.zeroMQ)
|
||||||
}
|
}
|
||||||
|
|
@ -482,8 +482,6 @@ object Dependency {
|
||||||
object V {
|
object V {
|
||||||
val Camel = "2.8.0"
|
val Camel = "2.8.0"
|
||||||
val Jackson = "1.8.0"
|
val Jackson = "1.8.0"
|
||||||
val JavaxServlet = "3.0"
|
|
||||||
val Jersey = "1.3"
|
|
||||||
val Jetty = "7.4.0.v20110414"
|
val Jetty = "7.4.0.v20110414"
|
||||||
val Logback = "0.9.28"
|
val Logback = "0.9.28"
|
||||||
val Netty = "3.3.0.Final"
|
val Netty = "3.3.0.Final"
|
||||||
|
|
@ -494,7 +492,6 @@ object Dependency {
|
||||||
val Slf4j = "1.6.4"
|
val Slf4j = "1.6.4"
|
||||||
val Spring = "3.0.5.RELEASE"
|
val Spring = "3.0.5.RELEASE"
|
||||||
val Zookeeper = "3.4.0"
|
val Zookeeper = "3.4.0"
|
||||||
val PlayMini = "2.0-RC1-SNAPSHOT"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile
|
// Compile
|
||||||
|
|
@ -530,8 +527,7 @@ object Dependency {
|
||||||
val zkClient = "zkclient" % "zkclient" % "0.3" // ApacheV2
|
val zkClient = "zkclient" % "zkclient" % "0.3" // ApacheV2
|
||||||
val zookeeper = "org.apache.hadoop.zookeeper" % "zookeeper" % V.Zookeeper // ApacheV2
|
val zookeeper = "org.apache.hadoop.zookeeper" % "zookeeper" % V.Zookeeper // ApacheV2
|
||||||
val zookeeperLock = "org.apache.hadoop.zookeeper" % "zookeeper-recipes-lock" % V.Zookeeper // ApacheV2
|
val zookeeperLock = "org.apache.hadoop.zookeeper" % "zookeeper-recipes-lock" % V.Zookeeper // ApacheV2
|
||||||
val zeroMQ = "org.zeromq" %% "zeromq-scala-binding" % "0.0.3" // ApacheV2
|
val zeroMQ = "org.zeromq" %% "zeromq-scala-binding" % "0.0.3" // ApacheV2
|
||||||
val playMini = "com.typesafe" % "play-mini_2.9.1" % V.PlayMini
|
|
||||||
|
|
||||||
// Provided
|
// Provided
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue