I am learning how to work with both Scala and the PlayFramework for a project I am doing. Since my objective is similar to the one presented in this blog I am basing my code in it. However while trying to replicate that code I am finding an error in this
def stats( id: String ) = WebSocket.async[JsValue] { request =>
Hosts.hosts.find( _.id == id ) match {
case Some( host ) => Statistics.attach( host )
case None => {
val enumerator = Enumerator
.generateM[JsValue]( Promise.timeout( None, 1.second ) )
.andThen( Enumerator.eof )
Promise.pure( ( Iteratee.ignore[JsValue], enumerator ) )
}
}
}
The errors says value Pure is not a member of object play.api.libs.concurrent.Promise, initally I thought since this code was based on an older version of Play, something had changed. However I went to check the changelog and Promise seemed mostly unchanged, and according to the documentation Pure is still a member of it. It's probably something really simple but being new to this I am relatively confused on why this error is happening since the code should be tried and tested and still compatible with this version.
That's using a much older version of the Play libraries. Promise.pure was deprecated in 2.2.x, and removed in 2.3.x. You can use scala.concurrent.Future.successful instead.
Future.successful( ( Iteratee.ignore[JsValue], enumerator ) )
Related
From the documentation I can see that I should be able to use WriteResult.ok, WriteResult.code and WriteResult.n in order to understand errors and the number of updated documents but this isn't working. Here is a sample of what I'm doing (using reactiveMongoDB/Play JSON Collection Plugin):
def updateOne(collName: String, id: BSONObjectID, q: Option[String] = None) = Action.async(parse.json) { implicit request: Request[JsValue] =>
val doc = request.body.as[JsObject]
val idQueryJso = Json.obj("_id" -> id)
val query = q match {
case Some(_) => idQueryJso.deepMerge(Json.parse(q.get).as[JsObject])
case None => idQueryJso
}
mongoRepo.update(collName)(query, doc, manyBool = false).map(result => writeResultStatus(result))
}
def writeResultStatus(writeResult: WriteResult): Result = {
// NOT WORKING
if(writeResult.ok) {
if(writeResult.n > 0) Accepted else NotModified
} else BadRequest
}
Can I give an alternative approach here? You said:
"in order to understand errors and the number of updated documents but this isn't working"
Why you don't use the logging functionality that Play provides? The general idea is that:
You set the logging level (e.g., only warning and errors, or errors, etc.).
You could use the log to output a message in any case, either something is ok, or it is not.
Play saves the logs of your application while it is running.
You the maintainer/developer could look into the logs to check if there is any errors.
This approach open a great possibility in the future: you could save the logs into a third-party service and put monitoring functionalities on the top of it.
Now if we look at the documentation here, you see about different log levels, and how to use the logger.
I have to query an API with pagination. But because the API is weird I have to query it until the response is empty (contains no items).
Actually it is done in Java with a do-while :
do {
objects = doQuery(from);
from += 200
} while ( !objects.isEmpty() )
But I would like to convert it in scala. My first idea is to use a stream with a step and takeWhile :
Stream.iterate(0)(_+200)
.map( from => doQuery(from) )
.takeWhile( objects -> !objects.isEmpty )
But another changes make doQuery returns a Future. Thus I cannot do the test in takeWhile and have no best idea on how to do it (maybe a recursive call).
Hopefully this new code will be into an Akka actor that will tell another for each object (no need to return anything)
This gives you Stream[Future[(Boolean, T)]], where the first time will be false as soon as the rest will be empty. Not sure how to do the takeWhile without blocking.
Stream.iterate(0)(_+200)
.scanLeft(Future((true, List[Int]())))({case (prev, from) =>
prev.flatMap({case (cont, _) =>
if(cont) {
doQuery(from).map(res => (res.isEmpty, res.toList))
} else {
Future((false, List()))
}
})
})
If I started a process using Scala Process/ProcessBuilder. How can I get the pid of the process that was created?
I could not find any mention of the pid in the official docs:
http://www.scala-lang.org/api/2.10.4/index.html#scala.sys.process.Process
http://www.scala-lang.org/api/2.10.4/index.html#scala.sys.process.ProcessBuilder
http://www.scala-lang.org/api/2.10.4/index.html#scala.sys.process.package
2016: same question; I've been clicking through related questions for a few minutes, but still couldn't find any solution that is generally agreed upon. Here is a Scala version inspired by LRBH10's Java code in the answer linked by wingedsubmariner:
import scala.sys.process.Process
def pid(p: Process): Long = {
val procField = p.getClass.getDeclaredField("p")
procField.synchronized {
procField.setAccessible(true)
val proc = procField.get(p)
try {
proc match {
case unixProc
if unixProc.getClass.getName == "java.lang.UNIXProcess" => {
val pidField = unixProc.getClass.getDeclaredField("pid")
pidField.synchronized {
pidField.setAccessible(true)
try {
pidField.getLong(unixProc)
} finally {
pidField.setAccessible(false)
}
}
}
// If someone wants to add support for Windows processes,
// this would be the right place to do it:
case _ => throw new RuntimeException(
"Cannot get PID of a " + proc.getClass.getName)
}
} finally {
procField.setAccessible(false)
}
}
}
// little demo
val proc = Process("echo 'blah blah blaaah'").run()
println(pid(proc))
WARNING: scala code runner is essentially just a bash script, so when you use it to launch scala programs, it will do thousand things before actually starting the java process. Therefore, the PID of the java-process that you are actually interested in will be much larger than what the above code snippet returns. So this method is essentially useless if you start your processes with scala. Use java directly, and explicitly add Scala library to the classpath.
The scala.sys.io.process classes are wrappers around the Java classes for starting processes, and unfortunately it is difficult to obtain the PID from this API. See the stackoverlow question for this, How to get PID of process I've just started within java program?.
When trying to update a couchbase document that was previously deleted the update call fails with a CASResponse.EXISTS code rather than a CASResponse.NOT_FOUND. However a call to get the previously deleted key immediately after the delete is complete returns null as expected:
def main(args: Array[String]){
val uris = List(URI.create("http://localhost:8091/pools"))
val client = new CouchbaseClient(uris, "test", "password")
client.add("asdf", "qwer").get
client.delete("asdf").get
val result = client.asyncGets("asdf").get
assert(result == null)
val response = client.asyncCAS("asdf", 1, "bar").get
response match {
case CASResponse.NOT_FOUND => println("Document Not Found")
case CASResponse.EXISTS => println("Document exists")
}
}
Adding Thread.sleep(1000) before the update call fixes the problem, so my question is this the expected behaviour of Couchbase?
Couchbase version is 2.2.0 enterprise edition (build-821), using the Java client version 1.2.1 in Scala 2.10.2.
Thanks
I think you probably want to do:
client.delete(key, PersistTo.MASTER).get();
You can change the enum PersistTo to other options such as ONE,TWO,THREE (i.e. Master plus additional numbers of nodes). I tested your example with only one node and the above worked for me.
Have you tried use client.gets() instead of client.asyncGets().get() and client.cas() instead of client.asyncCAS().get()? I think it can solve your problem.
I'm using Twitter's logging framework in Scala, version 6.5.0.
I've written a toy usage example:
import com.twitter.logging._
object TestFormatter extends Formatter
{
override def format( record : java.util.logging.LogRecord) = "TEST %s> ".format( record.getLevel.getName ) + formatText(record) + lineTerminator
}
object Main extends App
{
override def main( args : Array[String] ) : Unit =
{
// obtain a logger
val log = Logger.get( getClass )
// clear any existing message handlers
Logger.clearHandlers
// add a new handler, using my toy formatter
log.addHandler( ConsoleHandler( TestFormatter, Some( Level.DEBUG ) )() )
// log a couple of test messages
log.debug( "DEBUG LOG" )
log.warning( "WARNING LOG" )
}
}
The class TestFormatter isn't really necessary, but will help to highlight my problem. I'm pretty sure that what I should see from this by way of output from this code is something along the lines of:
TEST DEBUG> DEBUG LOG
TEST WARNING> WARNING LOG
However, what I actually get is:
TEST WARNING> WARNING LOG
WARNING: WARNING LOG
This raises two issues:
Why has some other handler also handled my warning message, despite me clearing existing handlers? - SOLVED
Why, when I've set the level of my logger to debug, has the message I logged at debug level not been handled?
If anyone could throw any light on either of these problems, I'd be most grateful.
For the first question, you can change your code to use Logger.clearHandlers to get rid of the additional handler.
Second question: you could add log.setLevel(Level.DEBUG) to get the output you are expecting. As for what setting the level of the handler does, it doesn't seem to control anything. So it is bug; at the very least a documentation bug in their library.
As it turns out, I wasn't using the framework in the expected manner. I've found that the LoggerFactory class is the way that you're supposed to make changes to your loggers. My main method should have been as follows:
override def main( args : Array[String] ) : Unit =
{
val log = Logger.get( "" )
LoggerFactory(
node = "",
level = Some( Level.DEBUG ),
handlers = List( ConsoleHandler( TestFormatter, Some( Level.DEBUG ) ) )
).apply()
log.debug( "DEBUG LOG" )
log.warning( "WARNING LOG" )
}
I'm still not entirely sure why the original code doesn't work, especially since the source of LoggerFactory seems to suggest that it registers the new Handlers by calling addHandler().
However, the output of this main method is:
TEST DEBUG> DEBUG LOG
TEST WARNING> WARNING LOG
which is what I was looking for.