How do I leveraging SLF4J varargs logging in Play2.1 framework? - scala

SLF4J's varargs on the logging calls are quite useful in my Java work
Logger log = LoggerFactory.getLogger( getClass() );
log.debug( "Hello, {}. The current time is {}", "robert", new Date() );
Attempting to do this simple example in Play 2.1 Framework/Scala and I run into the compiler rejecting me.
import play.api._
import play.api.mvc._
import org.slf4j.LoggerFactory
object Application extends Controller {
val log: org.slf4j.Logger = LoggerFactory.getLogger(getClass())
def hb = Action {
val message = makeMessage()
// COMPILER HATES THIS: ambiguous reference compiler error here
log.info("Hello {}. The current time is {}", "robert", new java.util.Date() )
Ok(message)
}
def makeMessage(): String = { return "stuff" }
}
[dm2-server] $ compile
[info] Compiling 2 Scala sources to /Users/bobk/work/dm2-server/target/scala-2.10/classes...
[error] /Users/bobk/work/dm2-server/app/controllers/Application.scala:16: ambiguous reference to overloaded definition,
[error] both method info in trait Logger of type (x$1: String, x$2: <repeated...>[Object])Unit
[error] and method info in trait Logger of type (x$1: String, x$2: Any, x$3: Any)Unit
[error] match argument types (String,String,java.util.Date)
[error] log.info("Hello {}. The current time is {}", "robert", new java.util.Date() )
[error] ^
[error] one error found
[error] (compile:compile) Compilation failed
[error] Total time: 1 s, completed Jun 6, 2013 10:54:41 AM
What is that error and how do I overcome it to call through to the SLF4J API? If I can't do that, how can I use the Play 2.1 Logging Framework to get varargs on my logging calls? Something is not right in Scala-land.

What version of SLF4J are you using? If you can go back to 1.6.6 or later, you can avoid this issue in ambiguity. Those two signatures unfortunately look the exact same to scala and the compiler can't seem to differentiate which one you mean. The common suggestion is to roll back to a version of SLF4J (if even possible for you) where this overloaded method ambiguity will not exist. More info can be found at the links below:
https://groups.google.com/forum/?fromgroups#!topic/scala-language/ms4IVIu-xGw
https://github.com/typesafehub/scalalogging/issues/16

The "quick fix" for this is as follows:
Just force the last argument to be type Any and that resolves the compiler's issue(s) (and makes for slightly less code...)
logger.debug("hello {} / {} ", "Hello", "World":Any)
Or in your case:
log.info("Hello {}. The current time is {}", "robert", new java.util.Date():Any)

Related

scala inherited value do not find

Scala version 2.11.8
I have parent class
abstract class FR(externalId:String, code:String, message:String) extends Serializable {
val this.externalId=externalId;
val this.code = code;
val this.message = message;
def toString:String={
return "FRworks";
}
}
The child class is:
class RD extends FR {
def this(lpTransaction:LPTransaction)={
externalId =lpTransaction.getField("somethinghere").toString
...
}
}
The error is:
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=256m; support was removed in 8.0
[info] Loading project definition from F:\workspace\frankcheckAPI\project
[info] Set current project to frankcheckapi (in build file:/F:/workspace/frankcheckAPI/)
[info] Compiling 20 Scala sources to F:\workspace\frankcheckAPI\target\scala-2.11\classes...
[error] F:\workspace\frankcheckAPI\src\main\scala\com\cardaccess\fraudcheck\RD.scala:9: 'this' expected but identifier found.
[error] externalId =lpTransaction.getField("somethinghere").toString
[error] ^
[error] one error found
[error] (compile:compileIncremental) Compilation failed
when I add this in front of externalId the error still:
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=256m; support was removed in 8.0
[info] Loading project definition from F:\workspace\frankcheckAPI\project
[info] Set current project to frankcheckapi (in build file:/F:/workspace/frankcheckAPI/)
[info] Compiling 20 Scala sources to F:\workspace\frankcheckAPI\target\scala-2.11\classes...
[error] F:\workspace\frankcheckAPI\src\main\scala\com\cardaccess\fraudcheck\ReDFraudCheckResponse.scala:9: '}' expected but '.' found.
[error] this.externalId =lpTransaction.getField("somethinghere").toString
[error] ^
[error] F:\workspace\frankcheckAPI\src\main\scala\com\cardaccess\fraudcheck\ReDFraudCheckResponse.scala:12: eof expected but '}' found.
[error] }
[error] ^
[error] two errors found
[error] (compile:compileIncremental) Compilation failed
Your code is very Java-influenced. There are couple of things wrong here; when you fix the obvious ones like missing class parameters in RD it boils down to not being able to reassign to val.
Let me give you an improved, Scala-fied version of the whole code.
abstract class FR(val externalId: String, val code: String, val message: String) extends Serializable
// dummy class
class LPTransaction {
def getField(s: String) = s
}
class RD(externalId: String, code: String, message: String) extends FR(externalId, code, message) {
def this(lpTransaction: LPTransaction) = {
this(lpTransaction.getField("somethinghere").toString, "defaultCode", "defaultMessage")
}
println(externalId)
}
val a = new RD(new LPTransaction) // prints "somethinghere"
Main improvements are:
You don't need private fields to be populated using arguments in the constructor. Scala favors immutability. Make your class arguments "vals", this means they will be available as public fields (instead of getters you will access them directly; this is contrary to OOP's encapsulation principle, but here it's ok because nobody can mess with them anyway since they are immutable; they may only be fetched)
Your subclass RD should be taking same fields as parameters as its parent class. Of course, you can then define an auxiliary constructor that takes only LPTransaction, but then you need to feed the parent class with some default values for the other parameters.
The rest kind of follows from this. I added the dummy implementation of LPTransaction to be able to compile. I also threw in a println statement in RD class just for the sake of example. Feel free to ask if something's not clear.
//scala automatically generates getters for passed in params. No need to set them explicitly. For immutable params use val, for mutable use var
abstract class FR(var externalId:String, val code:String, val message:String) extends Serializable {
//need to use override annotation for superclass methods
override def toString:String={
return "FRworks";
}
}
// notice how constructor parameters are passed to the base class when defining the child class.
class RD extends FR("someID","code","msg") {
def printId() = println(externalId)
}
val x = new RD
x.externalId = "new ID" //works because externalId is var (mutable)
x.code = "new code" //error because code is val (immutable)
x.printId //correctly prints the external id: someID

org.specs2.mock.Mockito matchers are not working as expected

Here is the code I am trying to run:
import org.specs2.mock.Mockito
import org.specs2.mutable.Specification
import org.specs2.specification.Scope
import akka.event.LoggingAdapter
class MySpec extends Specification with Mockito {
"Something" should {
"do something" in new Scope {
val logger = mock[LoggingAdapter]
val myVar = new MyClassTakingLogger(logger)
myVar.doSth()
there was no(logger).error(any[Exception], "my err msg")
}
}
}
When running this, I get the following error:
[error] org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
[error] Invalid use of argument matchers!
[error] 2 matchers expected, 1 recorded:
[error] -> at org.specs2.mock.mockito.MockitoMatchers$class.any(MockitoMatchers.scala:47)
[error]
[error] This exception may occur if matchers are combined with raw values:
[error] //incorrect:
[error] someMethod(anyObject(), "raw String");
[error] When using matchers, all arguments have to be provided by matchers.
[error] For example:
[error] //correct:
[error] someMethod(anyObject(), eq("String by matcher"));
Which would make a lot of sense, but neither eq("my err msg") nor equals("my err msg") does the job as I get an error. What am I missing?
When you are using matchers to match parameters you have to use them for all parameters. as the all arguments have to be provided by matchers indicates.
Moreover if you use a specs2 matcher it needs to be strongly-typed. equals is a Matcher[Any] but there is no conversion from Matcher[Any] to a String which is what method accepts.
So you need a Matcher[T] or a Matcher[String] in your case. If you just want to test for equality, the strongly-typed matcher is ===
there was no(logger).error(any[Exception], ===("hey"))
I would like to add that you should be wary of default arguments, i.e. if using matchers when stubbing methods, make sure to pass argument matchers for all arguments, because default arguments will almost certainly have constant values - causing this same error to appear.
E.g. to stub the method
def myMethod(arg1: String, arg2: String arg3: String = "default"): String
you cannot simply do
def myMethod(anyString, anyString) returns "some value"
but you also need to pass an argument matcher for the default value, like so:
def myMethod(anyString, anyString, anyString) returns "some value"
Just lost half an hour figuring this out :)

Scala Spec2 Mockito: Argument matchers with complex types

I'm trying to write a mock for a web service with Mockito. The mock should simulate a POST request using the play WS library.
/**
* Mock for the Web Service
*/
case class WSMock() extends Mockito {
val wsRequestHolder: play.api.libs.ws.WS.WSRequestHolder = mock[play.api.libs.ws.WS.WSRequestHolder]
val wsResponse: play.api.libs.ws.Response = mock[play.api.libs.ws.Response]
wsResponse.status returns 200
wsResponse.body returns "BODY RESP FROM WS"
val futureResponse = scala.concurrent.Future { wsResponse }
wsRequestHolder.post(any[Map[String,Seq[String]]]) returns futureResponse
}
When running the test I get the following error:
[error] InvalidUseOfMatchersException:
[error] Invalid use of argument matchers!
[error] 3 matchers expected, 1 recorded:
[error] -> at org.specs2.mock.mockito.MockitoMatchers$class.any(MockitoMatchers.scala:24)
[error]
[error] This exception may occur if matchers are combined with raw values:
[error] //incorrect:
[error] someMethod(anyObject(), "raw String");
[error] When using matchers, all arguments have to be provided by matchers.
[error] For example:
[error] //correct:
[error] someMethod(anyObject(), eq("String by matcher"));
[error]
[error] For more info see javadoc for Matchers class.
It looks to me as the any[...] expression using a complex type (with nested type parameters) does not correctly get resolved into a matcher. However, I don't see where the raw type comes into play.
What is the proper way to specify such a matcher for a parameter Map[String,Seq[String]]?
Thanks a lot!
wsRequestHolder.post(any[Map[String,Seq[String]]]) returns futureResponse
Note that post actually has a couple extra implicit parameters there:
def post [T] (body: T)(implicit wrt: Writeable[T], ct: ContentTypeOf[T]):
Promise[Response]
...which probably need to be matched explicitly, as in this spec2-users thread.
It seems the wsRequestHolder.post method requires three parameters, so Mockito expects you to send three (e.g. any[]) matchers, but you provided matchers for only one of them.

Explain why import scala.Predef.String fails build

I found very strange issue with Scala import.
I wrote sample class:
package test_scala_predef
object App extends App {
classOf[T]
println( "Hello World!" )
}
class T {
}
This class compiles without any errors.
But, if I add
import scala.Predef.String
then I get compilation errors:
[INFO] Compiling 1 source files to /home/uthark/src/_/test_scala_predef/target/classes at 1374028063588
[ERROR] /home/uthark/src/_/test_scala_predef/src/main/scala/test_scala_predef/App.scala:10: error: not found: value classOf
[INFO] classOf[T]
[INFO] ^
[ERROR] /home/uthark/src/_/test_scala_predef/src/main/scala/test_scala_predef/App.scala:11: error: not found: value println
[INFO] println( "Hello World!" )
[INFO] ^
[ERROR] two errors found
I have an idea, that after I add specific import from scala.Predef, then implict import of scala.Predef._ is not added. But I can't find anything about it in the Scala documentation. Can anyone point me to the relevant section in documentation?
I checked Scala Language Specification (PDF), section 12.5 covering scala.Predef but also didn't find any related.
I use latest stable scala version available (2.10.2 at the moment)
I found the answer in the sources.
https://github.com/scala/scala/blob/master/src/compiler/scala/tools/nsc/typechecker/Contexts.scala:
/** List of symbols to import from in a root context. Typically that
* is `java.lang`, `scala`, and [[scala.Predef]], in that order. Exceptions:
*
* - if option `-Yno-imports` is given, nothing is imported
* - if the unit is java defined, only `java.lang` is imported
* - if option `-Yno-predef` is given, if the unit body has an import of Predef
* among its leading imports, or if the tree is [[scala.Predef]], `Predef` is not imported.
*/
protected def rootImports(unit: CompilationUnit): List[Symbol] = {
assert(definitions.isDefinitionsInitialized, "definitions uninitialized")
if (settings.noimports) Nil
else if (unit.isJava) RootImports.javaList
else if (settings.nopredef || treeInfo.noPredefImportForUnit(unit.body)) {
debuglog("Omitted import of Predef._ for " + unit)
RootImports.javaAndScalaList
}
else RootImports.completeList
}
This answers my question.
The key sentence is this:
Exceptions: ... if the unit body has an import of Predef among its leading imports
Also, in chat on #scala irc it was suggested to create bug in Scala issue tracking system, so I did it. https://issues.scala-lang.org/browse/SI-7672

How to I override a Java varargs method in Scala?

I have a method defined in Java like:
void foo(int x, Thing... things)
I need to override that in Scala, but both of these give errors:
override def foo(x: Int, things: Thing*)
override def foo(x: Int, things: Array[Thing])
The errors refer to <repeated...> but I don't know what that is.
Update
Ugg... nevermind. I'm in 2.10.0, and I had mis-typed something and didn't have a method body. Then I got confused by this error message, which still seems a odd to me. In SBT:
> compile
[info] Compiling 1 Scala source to [...]/target/scala-2.10/classes...
[error] [...]/src/main/scala/Translator.scala:41: class MyMethodVisitor needs to be abstract, since method visitTableSwitchInsn is not defined
[error] (Note that org.objectweb.asm.Label* does not match <repeated...>[org.objectweb.asm.Label])
[error] class MyMethodVisitor extends MethodVisitor (Opcodes.ASM4) {
[error] ^
The problem is that my visitTableSwitchInsn simply lacks a body, but the error suggests that the problem is the type of the varargs parameter.
Java:
package rrs.scribble;
public
class VA1
{
public int va1(int... ints) {
return ints.length;
}
}
Scala:
package rrs.scribble
class VA1S
extends VA1
{
override
def va1(ints: Int*): Int =
ints.length * 2
}
SBT:
> ~compile
[info] Compiling 1 Scala source and 1 Java source to …/scribble/target/scala-2.10/classes...
[success] Total time: 4 s, completed Jan 15, 2013 3:48:14 PM
1. Waiting for source changes... (press enter to interrupt)
This is Scala 2.10, which is consistent with #TravisBrown's comment.