I am creating a user registration module. On submission (using JSON), I want to check if the JSON parsed correctly. If there is a problem in JSON, I want to return error. If the JSON is correct, I want to check if the user already exists (look at firstname). Data is in MongoDB. I am using ReactiveMongoPlugin 0.10. I will use 'one' method which returns Future[Option[BSONDocument]]. How do I wait for this Future to finish before the Action completes?
Approach 1 - use Action and try to handle result of Future myself. Code doesn't compile and dont know how to wait for the Future to finish
def registrationRequest = Action(parse.json) { request => {
Logger.debug("received message:" + request)
Logger.debug("received message:" + request.body)
val jr:JsResult[User2] = request.body.validate[User2]
Logger.debug( "jr is "+jr)
jr match {
case s:JsSuccess[User2] => {
val user = s.get
Logger.debug("opening database connection")
val driver = new MongoDriver()
val connection = driver.connection(List("localhost"))
val db = connection.db("website-db")
val collection = db.collection[BSONCollection]("users")
// the data from client is a JSON of type {user:{firstname:"name"}}. I have created code to parse the JSON
val query = BSONDocument("user"-> BSONDocument("firstname"->user.firstname))
Logger.debug("query is:"+query)
val result = collection.find(query).one
I want to now wait for result and return either an Ok(Json.toJson(ack)) or BadRequest(Json.toJson(ack)). How do i do that? I have written following code but I am stuck at two points (a) will the code wait for future to finish (b) onComplete returns Unit but Play's Action requires play.api.mvc.Result. How do I do that?
//I guess data would be Success or Failure
result onComplete ( data =>
data match {
//If Success, value would be Some or None
case Success(value) => {
value match {
case None => { //no record. Can add
Logger.debug("No record from mongo: Can add")
val ack = Acknowledgment (1, "Welcome " + user.firstName + " " + user.lastName)
Logger.debug ("success ack Json:" + Json.toJson (ack) )
Ok (Json.toJson (ack) )
}
case Some(x) => { //duplicae record
Logger.debug("error from Mongo. Duplicate:"+x)
val ack = Acknowledgment(0,"duplicate: "+x.toString())
Logger.debug("fail ack:"+Json.toJson(ack))
BadRequest(Json.toJson(ack))
}
}
}
case Failure (e)=> {
Logger.debug("error from Mongo."+e)
val ack = Acknowledgment(0,"MongoDB Error: "+e.toString())
Logger.debug("fail ack:"+Json.toJson(ack))
BadRequest(Json.toJson(ack))
}
}) //onComplete returns Unit. Action needs play.api.mvc.Result
case f:JsError => {
Logger.debug("error: "+JsError.toFlatJson(f))
val ack = Acknowledgment(0,JsError.toFlatJson(f).toString())
Logger.debug("fail ack:"+Json.toJson(ack))
BadRequest(Json.toJson(ack))
}
}
}
Approach 2 - I read that I should use Action.async but I am unable to fit the pieces together. The 2nd approach I followed was to use Action.Async but the code didnt compile because it expects Future[SimpleResult]
def registrationRequest = Action.async(parse.json) { request => {
Logger.debug("received message:" + request)
Logger.debug("received message:" + request.body)
val jr:JsResult[User2] = request.body.validate[User2]
Logger.debug( "jr is "+jr)
jr match {
case s:JsSuccess[User2] => {
val user = s.get
Logger.debug("opening database connection")
val driver = new MongoDriver()
val connection = driver.connection(List("localhost"))
val db = connection.db("website-db")
val collection = db.collection[BSONCollection]("users")
val query = BSONDocument("user"-> BSONDocument("firstname"->user.firstName))
Logger.debug("query is:"+query)
//result is of type Future[Option[BSONDocument]]
val result = collection.find(query).one
result.map(option => option match {
case None => {
//no record. Can add
Logger.debug("No record from mongo: Can add")
val ack = Acknowledgment(1, "Welcome " + user.firstName + " " + user.lastName)
Logger.debug("success ack Json:" + Json.toJson(ack))
Ok(Json.toJson(ack))
}
case Some(x) => {
//duplicae record
Logger.debug("error from Mongo. Duplicate:" + x)
val ack = Acknowledgment(0, "duplicate: " + x.toString())
Logger.debug("fail ack:" + Json.toJson(ack))
BadRequest(Json.toJson(ack))
}
}
)
}
case f:JsError => {
Logger.debug("error: "+JsError.toFlatJson(f))
val ack = Acknowledgment(0,JsError.toFlatJson(f).toString())
Logger.debug("fail ack:"+Json.toJson(ack))
BadRequest(Json.toJson(ack)) //Action.async expect scala.concurrent.Future[play.api.mvc.SimpleResult]
}
}
}
The solution is to use Action.async which returns Future[SimpleResult]. Inside the code use map and flatMap on Future to return Future[SimpleResult].
Related
I am new to Scala and am using the method def process however getting build errors like "method wasSuccessful in class IOResult is deprecated (since 2.6.0): status is always set to Success(Done)
case Success((sftpResult, updateList)) if sftpResult.wasSuccessful =>". My question is how would I then implement this in my code? I apologise if the question seems stupid.
override def process(changedAfter: Option[ZonedDateTime], changedBefore: Option[ZonedDateTime])
(implicit fc: FlowContext): Future[(IOResult, Seq[Operation.Value])] = {
val now = nowUtc
val accountUpdatesFromDeletions: Source[AccountUpdate, NotUsed] =
dbService
.getDeleteActions(before = now)
.map(deletionEventToAccountUpdate)
val result = getAccountUpdates(changedAfter, changedBefore)
.merge(getErrorUpdates)
.merge(accountUpdatesFromDeletions)
.mapAsync(4)(writer.writePSVString)
.viaMat(creditBureauService.sendUpdate)(Keep.right)
.mapAsync(4)(au =>
for {
_ <- dbService.performUpdate(au)
_ <- performActionsDelete(now, au)
} yield au.operation
)
.toMat(Sink.seq)(Keep.both)
.withAttributes(ActorAttributes.supervisionStrategy(decider))
.run()
tupleFutureToFutureTuple(result) andThen {
case Success((sftpResult, updateList)) if sftpResult.wasSuccessful =>
val total = updateList.size
val deleted = updateList.count(_ == Operation.DELETED)
val updated = updateList.count(_ == Operation.UPDATED)
val inserted = updateList.count(_ == Operation.INSERTED)
log.info(s"SUCCESS! Uploaded $total accounts to Equifax.")
log.info(s"There were $deleted deletions, " +
s"$updated updates and " +
s"$inserted insertions to the database")
monitor.gauge("upload.process.batch.successful.total", total)
monitor.gauge("upload.process.batch.successful.deleted", deleted)
monitor.gauge("upload.process.batch.successful.updated", updated)
monitor.gauge("upload.process.batch.successful.inserted", inserted)
case Success((sftpResult, _)) if !sftpResult.wasSuccessful =>
val sw = new StringWriter
sftpResult.getError.printStackTrace(new PrintWriter(sw))
log.error("Failed to send data: " + sw.toString)
monitor.gauge("upload.process.batch.sftp.failed", 1)
case Failure(e: Throwable) =>
val sw = new StringWriter
e.printStackTrace(new PrintWriter(sw))
log.error(sw.toString)
monitor.gauge("upload.process.batch.failed", 1)
}
}
Both wasSuccessful and getError have been deprecated since 2.6. Since IOResult is in general wrapped in a Future which by itself already tells whether the I/O operation is a Success or Failure, the status parameter of a successfully returned IOResult is in essence redundant, thus is now always set to Success[Done].
Given that, you could simplify/refactor your case matching snippet to something like below:
tupleFutureToFutureTuple(result) andThen {
case Success((sftpResult, updateList)) =>
val total = updateList.size
val deleted = updateList.count(_ == Operation.DELETED)
val updated = updateList.count(_ == Operation.UPDATED)
val inserted = updateList.count(_ == Operation.INSERTED)
log.info(s"SUCCESS! Uploaded $total accounts to Equifax.")
log.info(s"There were $deleted deletions, " +
s"$updated updates and " +
s"$inserted insertions to the database")
monitor.gauge("upload.process.batch.successful.total", total)
monitor.gauge("upload.process.batch.successful.deleted", deleted)
monitor.gauge("upload.process.batch.successful.updated", updated)
monitor.gauge("upload.process.batch.successful.inserted", inserted)
case Failure(e: Throwable) =>
val sw = new StringWriter
e.printStackTrace(new PrintWriter(sw))
log.error(sw.toString)
monitor.gauge("upload.process.batch.failed", 1)
}
I have an Akka server who is asking the mallet file (some output) from an actor.
However in mallet actor code, several steps are done.
In which files are taken, modified, new files are created and saved in resource directory couple of times.
I need to run the actor sequentially so I am using map in calling future.
However the job is getting NullPointerException as there is no file for next future.
And as soon as I am stopping the server. all the files are getting generated in resources directory.
I need the files in resources directory as soon as individual future is completed. Please suggest
Below is the code of my server
lazy val routes: Route = apiRoutes
Configuration.parser.parse(args,Configuration.ConfigurationOptions()) match {
case Some(config) =>
val serverBinding: Future[Http.ServerBinding] = Http().bindAndHandle(routes, config.interface, config.port)
serverBinding.onComplete {
case Success(bound) =>
println(s"Server online at http://${bound.localAddress.getHostString}:${bound.localAddress.getPort}/")
case Failure(e) =>
log.error("Server could not start: ", e)
system.terminate()
}
case None =>
system.terminate()
}
Await.result(system.whenTerminated, Duration.Inf)
}
Below is the code of receive function.
def receive: Receive = {
case GetMalletOutput(malletFile) => createMalletResult(malletFile).pipeTo(sender())
}
def createMalletResult(malletFile: String): Future[MalletModel] = {
//sample malletResult
val topics = Array(Topic("1", "2").toJson)
var mM: Future[MalletModel] = Future{MalletModel("contentID", topics)}
//first Future to save file in resource
def saveFile(malletFile: String): Future[String] = Future {
val res = MalletResult(malletFile)
val converted = res.Score.parseJson.convertTo[MalletRepo]
val fileName = converted.ContentId
val fileTemp = new File("src/main/resources/new_corpus/" + fileName)
val output = new BufferedWriter(new FileWriter("src/main/resources/new_corpus/" + fileName))
output.write(converted.ContentText)
//output.close()
malletFile
}
//Second Future to used the resource file and create new one
def t2v(malletFile: String): Future[String] = Future{
val tmpDir = "src/main/resources/"
logger.debug(tmpDir.toString)
logger.debug("t2v Started")
Text2Vectors.main(("--input " + tmpDir + "new_corpus/ --keep-sequence --remove-stopwords " + "--output " + tmpDir + "new_corpus.mallet --use-pipe-from " + tmpDir + "corpus.mallet").split(" "))
logger.debug("text2Vector Completed")
malletFile
}
//another future to take file from resource and save in the new file back in resource
def infer(malletFile: String): Future[String] = Future {
val tmpDir = "src/main/resources/"
val tmpDirNew = "src/main/resources/inferResult/"
logger.debug("infer started")
InferTopics.main(("--input " + tmpDir + "new_corpus.mallet --inferencer " + tmpDir + "inferencer " + "--output-doc-topics " + tmpDirNew + "doc-topics-new.txt --num-iterations 1000").split(" "))
logger.debug("infer Completed")
malletFile
}
//final future to return the requested output using the saved future
def response(malletFile: String): Future[MalletModel] = Future{
logger.debug("response Started")
val lines = Source.fromResource("src/main/resources/inferResult/doc-topics-new.txt")
.getLines
.toList
.drop(1) match {
case Nil => List.empty
case x :: xs => x.split(" ").drop(2).mkString(" ") :: xs
}
logger.debug("response On")
val result = MalletResult(malletFile)
val convert = result.Score.parseJson.convertTo[MalletRepo]
val contentID = convert.ContentId
val inFile = lines.mkString(" ")
val a = inFile.split(" ").zipWithIndex.collect { case (v, i) if (i % 2 == 0) =>
(v, i)
}.map(_._1)
val b = inFile.split(" ").zipWithIndex.collect { case (v, i) if (i % 2 != 0) =>
(v, i)
}.map(_._1)
val paired = a.zip(b) // [(s,t),(s,t)]
val topics = paired.map(x => Topic(x._2, x._1).toJson)
logger.debug("validating")
logger.debug("mallet results...")
logger.debug("response Done")
MalletModel(contentID, topics)
}
//calling one future after another to run future sequntially
val result: Future[MalletModel] =
saveFile(malletFile).flatMap(malletFile =>
t2v(malletFile).flatMap(mf =>
infer(mf).flatMap(mf =>
response(mf))))
result
}
}
It seems I have got the answer myself.
The problem was I was trying to write the file in resource directory and reading the file back. to resolve the issue I wrote the file in tmpDir and read it back with bufferreader instead of source. and bam it worked.
I am new to Akka and developed a sample Akka WebSocket server that streams a file's contents to clients using BroadcastHub (based on a sample from the Akka docs).
How can I measure the throughput (messages/second), assuming the clients are consuming as fast as the server?
// file source
val fileSource = FileIO.fromPath(Paths.get(path)
// Akka file source
val theFileSource = fileSource
.toMat(BroadcastHub.sink)(Keep.right)
.run
//Akka kafka file source
lazy val kafkaSourceActorStream = {
val (kafkaSourceActorRef, kafkaSource) = Source.actorRef[String](Int.MaxValue, OverflowStrategy.fail)
.toMat(BroadcastHub.sink)(Keep.both).run()
Consumer.plainSource(consumerSettings, Subscriptions.topics("perf-test-topic"))
.runForeach(record => kafkaSourceActorRef ! record.value().toString)
}
def logicFlow: Flow[String, String, NotUsed] = Flow.fromSinkAndSource(Sink.ignore, theFileSource)
val websocketFlow: Flow[Message, Message, Any] = {
Flow[Message]
.collect {
case TextMessage.Strict(msg) => Future.successful(msg)
case _ => println("ignore streamed message")
}
.mapAsync(parallelism = 2)(identity)
.via(logicFlow)
.map { msg: String => TextMessage.Strict(msg) }
}
val fileRoute =
path("file") {
handleWebSocketMessages(websocketFlow)
}
}
def startServer(): Unit = {
bindingFuture = Http().bindAndHandle(wsRoutes, HOST, PORT)
log.info(s"Server online at http://localhost:9000/")
}
def stopServer(): Unit = {
bindingFuture
.flatMap(_.unbind())
.onComplete{
_ => system.terminate()
log.info("terminated")
}
}
//ws client
def connectToWebSocket(url: String) = {
println("Connecting to websocket: " + url)
val (upgradeResponse, closed) = Http().singleWebSocketRequest(WebSocketRequest(url), websocketFlow)
val connected = upgradeResponse.flatMap{ upgrade =>
if(upgrade.response.status == StatusCodes.SwitchingProtocols )
{
println("Web socket connection success")
Future.successful(Done)
}else {
println("Web socket connection failed with error: {}", upgrade.response.status)
throw new RuntimeException(s"Web socket connection failed: ${upgrade.response.status}")
}
}
connected.onComplete { msg =>
println(msg)
}
}
def websocketFlow: Flow[Message, Message, _] = {
Flow.fromSinkAndSource(printFlowRate, Source.maybe)
}
lazy val printFlowRate =
Flow[Message]
.alsoTo(fileSink("output.txt"))
.via(flowRate(1.seconds))
.to(Sink.foreach(rate => println(s"$rate")))
def flowRate(sampleTime: FiniteDuration) =
Flow[Message]
.conflateWithSeed(_ ⇒ 1){ case (acc, _) ⇒ acc + 1 }
.zip(Source.tick(sampleTime, sampleTime, NotUsed))
.map(_._1.toDouble / sampleTime.toUnit(SECONDS))
def fileSink(file: String): Sink[Message, Future[IOResult]] = {
Flow[Message]
.map{
case TextMessage.Strict(msg) => msg
case TextMessage.Streamed(stream) => stream.runFold("")(_ + _).flatMap(msg => Future.successful(msg))
}
.map(s => ByteString(s + "\n"))
.toMat(FileIO.toFile(new File(file)))(Keep.right)
}
You could attach a throughput-measuring stream to your existing stream. Here is an example, inspired by this answer, that prints the number of integers that are emitted from the upstream source every second:
val rateSink = Flow[Int]
.conflateWithSeed(_ => 0){ case (acc, _) => acc + 1 }
.zip(Source.tick(1.second, 1.second, NotUsed))
.map(_._1)
.toMat(Sink.foreach(i => println(s"$i elements/second")))(Keep.right)
In the following example, we attach the above sink to a source that emits the integers 1 to 10 million. To prevent the rate-measuring stream from interfering with the main stream (which, in this case, simply converts every integer to a string and returns the last string processed as part of the materialized value), we use wireTapMat:
val (rateFut, mainFut) = Source(1 to 10000000)
.wireTapMat(rateSink)(Keep.right)
.map(_.toString)
.toMat(Sink.last[String])(Keep.both)
.run() // (Future[Done], Future[String])
rateFut onComplete {
case Success(x) => println(s"rateFut completed: $x")
case Failure(_) =>
}
mainFut onComplete {
case Success(s) => println(s"mainFut completed: $s")
case Failure(_) =>
}
Running the above sample prints something like the following:
0 elements/second
2597548 elements/second
3279052 elements/second
mainFut completed: 10000000
3516141 elements/second
607254 elements/second
rateFut completed: Done
If you don't need a reference to the materialized value of rateSink, use wireTap instead of wireTapMat. For example, attaching rateSink to your WebSocket flow could look like the following:
val websocketFlow: Flow[Message, Message, Any] = {
Flow[Message]
.wireTap(rateSink) // <---
.collect {
case TextMessage.Strict(msg) => Future.successful(msg)
case _ => println("ignore streamed message")
}
.mapAsync(parallelism = 2)(identity)
.via(logicFlow)
.map { msg: String => TextMessage.Strict(msg) }
}
wireTap is defined on both Source and Flow.
Where I last worked I implemented a performance benchmark of this nature.
Basically, it meant creating a simple client app that consumes messages from the websocket and outputs some metrics. The natural choice was to implement the client using akka-http client-side support for websockets. See:
https://doc.akka.io/docs/akka-http/current/client-side/websocket-support.html#singlewebsocketrequest
Then we used the micrometer library to expose metrics to Prometheus, which was our tool of choice for reporting and charting.
https://github.com/micrometer-metrics
https://micrometer.io/docs/concepts#_meters
I am writing an Action in Play which should add a Document in MongoDB if it doesn't exist already. I have tried two approaches but none of them are working. I am stuck around how to handle Future returned by reactivemongoplugin
The Action gets JSON data. It valides it. If JSON is OK, I check if the user exists (look at firstname). If it doesnt, I want to add it else return an error. I am unable to combine ReactiveMongoPlugin's 'find' and 'insert' methods in the same action
Approach 1** - This doesn't compile because the code returns scala.concurrent.Future[play.api.mvc.SimpleResult] while it requires play.api.mvc.SimpleResult. I know that it is because I am using a map inside a map.
def registrationRequest = Action.async(parse.json) { request => {
Logger.debug("received message:" + request)
Logger.debug("received message:" + request.body)
val jr: JsResult[User2] = request.body.validate[User2]
Logger.debug("jr is " + jr)
jr match {
case s: JsSuccess[User2] => {
val user = s.get
Logger.debug("opening database connection")
val driver = new MongoDriver()
val connection = driver.connection(List("localhost"))
val db = connection.db("website-db")
val collection = db.collection[BSONCollection]("users")
val query = BSONDocument("user.firstname" -> user.firstName)
Logger.debug("query is:" + query)
//result is of type Future[Option[BSONDocument]]
val findFuture:Future[Option[BSONDocument]] = collection.find(query).one
findFuture.map(option => option match {
case None => {
//no record. Can add
Logger.debug("No record from mongo: Can add")
val newUserDoc = BSONDocument (
"id" -> user.id,
"user" -> BSONDocument (
"firstname" -> user.firstName,
"lastname" -> user.lastName,
"email" -> BSONArray (user.email (0) ),
"password" -> user.password,
"address" -> BSONDocument (
"street" -> user.address.street,
"country" -> user.address.country
)
)
)
//addResult is of type Future[LastError]
//this code is problamatic. I am calling a map within a map which creates a Future[Future[Result]]. I need only Future[Result]
val insertResult = collection.insert (newUserDoc)
insertResult.map(le=>{
if(le.ok) {
Logger.debug("insertFuture map")
val ack = Acknowledgment(0, "insert success: ")
Logger.debug("insert success:" + Json.toJson(ack))
Ok(Json.toJson(ack))
}else {
Logger.debug("not inserting")
val ack = Acknowledgment (0, "duplicate: ")
Logger.debug ("fail ack:" + Json.toJson (ack) )
BadRequest (Json.toJson (ack) )
}
})}
case Some(x) => {
//duplicae record
Logger.debug("error from Mongo. Duplicate:" + x)
val ack = Acknowledgment(0, "duplicate: " + x.toString())
Logger.debug("fail ack:" + Json.toJson(ack))
BadRequest(Json.toJson(ack))
}
})
//findFutureResult is a Future[Int]
case f: JsError => Future.successful({
Logger.debug("error: " + JsError.toFlatJson(f))
val ack = Acknowledgment(0, JsError.toFlatJson(f).toString())
Logger.debug("fail ack:" + Json.toJson(ack))
BadRequest(Json.toJson(ack))
})
}
}
}
Approach 2 - In this approach, I broke the steps to avoid calling map within map. Following code is for JsSuccess part
case s: JsSuccess[User2] => {
val user = s.get
Logger.debug("opening database connection")
val driver = new MongoDriver()
val connection = driver.connection(List("localhost"))
val db = connection.db("website-db")
val collection = db.collection[BSONCollection]("users")
val query = BSONDocument("user.firstname" -> user.firstName)
Logger.debug("query is:" + query)
//result is of type Future[Option[BSONDocument]]
val findFuture:Future[Option[BSONDocument]] = collection.find(query).one
//findFutureResult is a Future[Int]
//to avoid calling map within map, I am creating single Futures which would convey result of one Future to another.
val findFutureResult:Future[Int] = findFuture.map(option => option match {
case None => {
//no record. Can add
Logger.debug("No record from mongo: Can add")
1 //return of 1 means record can be added
}
case Some(x) => {
//duplicae record
Logger.debug("error from Mongo. Duplicate:" + x)
2 //return of 2 means record cannot be added.
}
})
//this code would return LastError. the value of LastError can be used to check if insert was performed or not. Accordingly, I want to send OK or BadRequest
val insertFuture:Future[Future[LastError]] = findFutureResult.map(r => {r match {
case 1 => {
Logger.debug("findFutureResult map. Adding doc")
val newUserDoc = BSONDocument (
"id" -> user.id,
"user" -> BSONDocument (
"firstname" -> user.firstName,
"lastname" -> user.lastName,
"email" -> BSONArray (user.email (0) ),
"password" -> user.password,
"address" -> BSONDocument (
"street" -> user.address.street,
"country" -> user.address.country
)
)
)
//addResult is of type Future[LastError]
collection.insert (newUserDoc)
}
case 2 => Future.successful({
Logger.debug("findFutureResultmap. Not adding a duplicate")
LastError(false,None, None, None, None, 0,false )
})
}
})
//this is the problematic code. How do i check value of LastError? insertFuture is Future[Future[LastError]] and not Future[LastError]
insertFuture.map(lef=>{ lef.map(le=>{ // I cannot call map inside map as explained in approach 1
if(le.ok) {
Logger.debug("insertFuture map")
val ack = Acknowledgment(0, "insert success: ")
Logger.debug("insert success:" + Json.toJson(ack))
Ok(Json.toJson(ack))
}
else {
Logger.debug("not inserting")
val ack = Acknowledgment (0, "duplicate: ")
Logger.debug ("fail ack:" + Json.toJson (ack) )
BadRequest (Json.toJson (ack) )
}
})})
}
}
I know what the problem is in the code. I do not know how to solve it. I assume my approach is not bad - I want to check database before inserting in it but I am unable to fit it around reactivemongo apis and Futures
flatMap worked. Thank you Cyrille Corpet and cchantep
def registrationRequest = Action.async(parse.json) { request => {
Logger.debug("received message:" + request)
Logger.debug("received message:" + request.body)
val jr: JsResult[User2] = request.body.validate[User2]
Logger.debug("jr is " + jr)
jr match {
case s: JsSuccess[User2] => {
val user = s.get
Logger.debug("opening database connection")
val driver = new MongoDriver()
val connection = driver.connection(List("localhost"))
val db = connection.db("website-db")
val collection = db.collection[BSONCollection]("users")
val query = BSONDocument("user.firstname" -> user.firstName)
Logger.debug("query is:" + query)
//result is of type Future[Option[BSONDocument]]
val findFuture: Future[Option[BSONDocument]] = collection.find(query).one
val insertFuture: Future[Future[LastError]] = findFuture.map(option => option match {
case None => {
//no record. Can add
Logger.debug("No record from mongo: Can add")
//1
Logger.debug("findFutureResult map. Adding doc")
val newUserDoc = BSONDocument(
"id" -> user.id,
"user" -> BSONDocument(
"firstname" -> user.firstName,
"lastname" -> user.lastName,
"email" -> BSONArray(user.email(0)),
"password" -> user.password,
"address" -> BSONDocument(
"street" -> user.address.street,
"country" -> user.address.country
)
)
)
//addResult is of type Future[LastError]
collection.insert(newUserDoc)
}
case Some(x) => {
//duplicae record
Logger.debug("error from Mongo. Duplicate:" + x)
//2
Future.successful({
Logger.debug("findFutureResultmap. Not adding a duplicate")
LastError(false, None, None, None, None, 0, false)
})
}
})
insertFuture.flatMap(lef => {
lef.map(le => {
if (le.ok) {
Logger.debug("insertFuture map")
val ack = Acknowledgment(0, "insert success: ")
Logger.debug("insert success:" + Json.toJson(ack))
Ok(Json.toJson(ack))
}
else {
Logger.debug("not inserting")
val ack = Acknowledgment(0, "duplicate: ")
Logger.debug("fail ack:" + Json.toJson(ack))
BadRequest(Json.toJson(ack))
}
})
//findFutureResult is a Future[Int]
})
}
case f: JsError => Future.successful({
Logger.debug("error: " + JsError.toFlatJson(f))
val ack = Acknowledgment(0, JsError.toFlatJson(f).toString())
Logger.debug("fail ack:" + Json.toJson(ack))
BadRequest(Json.toJson(ack))
})
}
}
}
import java.net._
import java.io._
import java.util._
object Server2{
def displayUsageInfo = {
println("Parameters are:");
println(" 1) The port number that the server will listen on")
}
def main(args: Array[String]):Unit = {
if(args.length != 1) {
displayUsageInfo
}
else {
try {
val sock=new ServerSocket(Integer.parseInt(args(0)))
var finished = false
while (!finished){
println("waiting");
val connection=sock.accept()
println("connection from " + connection.getRemoteSocketAddress())
val is = connection.getInputStream()
val in = new BufferedReader(new InputStreamReader(is))
var line = in.readLine
while (line!=null && !finished) {
line match {
case "quit" => finished = true
case _ => println("Received " + line)
}
if(line == "<gia-sou/>"){
printf("he"); //here!!!!!!!!!!
}
if(!finished){
line = in.readLine
}
}
connection.close
}
} catch {
case e:BindException =>
println("Cannot bind to port. Is a server already running?")
case e:NumberFormatException =>
println("Port number should be an integer")
case e:IllegalArgumentException =>
println("The port number needs to be less than 65536")
case ex:Throwable => println("Exception: " +ex.toString())
}
}
}
}
I understand my knowledge on Scala is very low, I'm very new to this still. I would like to know when my server receives a message from the client, how do I send a message back? The place id like to put the message is where is say 'here'.
Please help thank you.
You can use whatever java provides, that is a PrintWriter
val connection=sock.accept()
println("connection from " + connection.getRemoteSocketAddress())
val printWriter = new PrintWriter(connection.getOutputStream, true)
printWriter.println("Whatever message you like")