home.


Tagged: scala


Scala: Using Scala's Try utility as an alternative to Java's Try/Catch/Finally

Let’s recall this standard SQL query code with the standard Java-esq try/catch/finally:

try {
  stmt = conn.createStatement
  rs = stmt.executeQuery(sql)
  // Deal with result set
  somethingToReturn
} catch {
  case e: Exception => ...
} finally {
  if(stmt!=null) stmt.close
  if(rs!=null) rs.close  
}

With scala.util.{Try,Failure,Sucess} it would be:

var insert = Try({
  stmt = conn.createStatement
  rs = stmt.executeQuery(sql)
  // Deal with result set
  somethingToReturn
})
if(stmt!=null) stmt.close
if(rs!=null) rs.close  
insert match {
  case Success(s) => s
  case Failure(e) => ...
}

The Try block has our code that may throw an exception.

Then we perform the code that was in the finally block after that.

And then finally we match if the Try value is a success or failure.

We’ve done a few things:

  • The finally code is no longer indented
  • Fewer lines starting with } keyword
  • The code that returns or fails is at the end now

This arguable makes things more readable.

scala scala-try

Jersey: Custom bean validation / ConstraintViolationException

With bean validation in Jersey, you get JSON errors like this:

[
  {
    "message":"may not be null",
    "messageTemplate":"{javax.validation.constraints.NotNull.message}",
    "path":"Hello.register.arg0.email",
    "invalidValue":null
  }
]

That’s great, but sometimes you have errors that appear after bean validation, such as when someone tries to register as an existing user.

You will only know about this when you talk to your data stores of existing users.

It would be nice to keep the above error format, but talk about more general errors. You do this by creating a custom ConstraintViolation and throwing a ConstraintViolationException.

The ConstraintViolationException takes a HashSet of ConstraintViolation. Let’s create a dummy ConstraintViolation in Scala (the Java version is obviously just about the same):

new ConstraintViolation[Object] {
  def getConstraintDescriptor(): ConstraintDescriptor[_] = null
  def getExecutableParameters(): Array[Object] = null
  def getExecutableReturnValue(): Object = null
  def getInvalidValue(): Object = "The invalid valid"
  def getLeafBean(): Object = null
  def getMessage(): String = "Something went wrong"
  def getMessageTemplate(): String = ""
  def getPropertyPath(): javax.validation.Path = new MyPropertyPath(argPath)
  def getRootBean(): Object = obj
  def getRootBeanClass(): Class[Object] = classOf[Object]
  def unwrap[U](t: Class[U]): U = t.newInstance
}

Most of the values above are empty, except for getInvalidValue, getMessage, getPropertyPath, getRootBean and getRootBeanClass

getInvalidValue displays the invalid value in the JSON above. getMessage displays the messages. getPropertyPath helps display path above: it’s just a dumy object with an dumy iterator and we use its toString method to fill out the rest of the "path"

The getRootBean gives us the name at the start of the "path" json reply. getRootBeanClass and unwrap need to be non-null but don’t seem to affect the JSON output.

Let’s put this altogether:

// Dumpy Path object: we only want its "toString"
class MyPropertyPath(var pathName: String) extends javax.validation.Path {
  def iterator():java.util.Iterator[javax.validation.Path.Node] = {
    return new java.util.Iterator[javax.validation.Path.Node] {
      def hasNext(): Boolean = false
      def next(): javax.validation.Path.Node = null
    }
  }
  override def toString:String = pathName
}

def customValidationErrorMessage(obj: Object, argPath: String, message: String, invalid: Object) = {
  var hs = new HashSet[ConstraintViolation[_]]
  hs.add(new ConstraintViolation[Object] {
    def getConstraintDescriptor(): ConstraintDescriptor[_] = null
    def getExecutableParameters(): Array[Object] = null
    def getExecutableReturnValue(): Object = null
    def getInvalidValue(): Object = invalid
    def getLeafBean(): Object = null
    def getMessage(): String = message
    def getMessageTemplate(): String = ""
    def getPropertyPath(): javax.validation.Path = new P(argPath)
    def getRootBean(): Object = obj
    def getRootBeanClass(): Class[Object] = classOf[Object]
    def unwrap[U](t: Class[U]): U = t.newInstance
  })
  throw new ConstraintViolationException("", hs)
}

If we call customValidationErrorMessage(register, "username", "Duplicate user", "dave") in a Jersey method, we’ll get this returned:

[
  {
    "message":"Duplicate user",
    "messageTemplate":"",
    "path":"RegisterUser.username",
    "invalidValue":"dave"
  }
]

Voila!

jersey bean-validation scala

Jersey: Bean validation with JSON return objects

Here’s how to get JSON error messages detailing validation errors in your REST input.

I’m doing this in Scala, but the Java version is obviously nearly the same.

First let’s look at our gradle dependencies, jaxrs, jetty, jackson, bean validation and our default logger.

 compile 'org.glassfish.jersey.bundles:jaxrs-ri:2.23.1'
 compile 'org.glassfish.jersey.containers:jersey-container-jetty-http:2.23.1'
 compile 'org.glassfish.jersey.media:jersey-media-json-jackson:2.16'
 compile 'org.glassfish.jersey.ext:jersey-bean-validation:2.23.2'
 compile "org.slf4j:slf4j-simple:1.6.1"

We need to turn bean validation error messages on in our ResourceConfig:

new ResourceConfig() {
  register(classOf[Hello])
  property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true)
}

We’ll define a class with @NonNull contraints, specifying Jackon will deserialise looking at fields not getters and setters for convenience.

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
class RegisterUser() {
  @NotNull var email: String = ""
  @NotNull var username: String = ""
  @NotNull var password: String = ""
}

Now the standard Jersey resource class, but now with the @Valid annotation on the POST object.

@Path("/") class Hello {
  @Path("register") @POST @Produces(Array(MediaType.APPLICATION_JSON))
  def register(@Valid register: RegisterUser) : RegisterUser = return register
}

Let’s put it altogether:

import javax.validation.constraints.{NotNull};
import javax.validation.{Valid};
import javax.ws.rs.core.{MediaType, UriBuilder}
import javax.ws.rs.{ApplicationPath, Path, Produces, POST}
import org.glassfish.jersey.server.{ResourceConfig, ServerProperties}
import org.glassfish.jersey.jetty.JettyHttpContainerFactory
import com.fasterxml.jackson.annotation.{JsonAutoDetect}

object rest {

  @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
  class RegisterUser() {
    @NotNull var email: String = ""
    @NotNull var username: String = ""
    @NotNull var password: String = ""
  }

  @Path("/") class Hello {
    @Path("register") @POST @Produces(Array(MediaType.APPLICATION_JSON))
    def register(@Valid register: RegisterUser) : RegisterUser = return register
  }

  def main(args: Array[String]) : Unit = {
    JettyHttpContainerFactory.createServer(
      UriBuilder.fromUri("http://localhost/").port(8901).build(),
      new ResourceConfig() {
        register(classOf[Hello])
        property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true)
      }
    )
  }

}

Let’s run the application using scala -cp "dependencies/*" valid.scala (dependencies is where my dependencies are. See this.

Here’s the curl command detailing the error in JSON:

curl -H "accept: application/json" -H "content-type: application/json" -X POST -d '{"username": "me", "email": null, "password":"p"}' http://localhost:8901/register
[{"message":"may not be null","messageTemplate":"{javax.validation.constraints.NotNull.message}","path":"Hello.register.arg0.email","invalidValue":null}]
jersey bean-validation jackson scala

Scala: Using gradle to build your project

If you want to use gradle to compile scala, ensure you have gradle, either in your system or via the gradle wrapper.

Create this build.gradle file:

apply plugin: 'scala'

repositories {
    mavenCentral()
}

dependencies {
  compile 'org.scala-lang:scala-library:2.11.6'
  // Your other deps here
}

sourceSets.main.scala.srcDirs = ['.']

This includes the scala library needed to compile, 2.11.6 is the latest scala at time of writing.

It also changes the defautl scala source file location to the current directory, but if you’re using src/main/scala there’s no need for this line.

Finally you have run gradle build or ./gradlew build if you’re using the gradle wrapper.

scala gradle

Scala: Bypassing "Ambiguous reference to overloaded definition" errors with reflection

Sometimes you’ll see something like this error, especially with var args methods, or methods with the Any type

Error:(41) ambiguous reference to overloaded definition,
both method someMethod in class SomeClass of type (x$1: String, etc)Unit
and  method someMethod in class SomeClass of type (x$1: String, etc)Unit
match argument types (String)

This means scala can’t differentiate between two methods.

This stackoverflow thread talks about it some more. And this scala bug report is working on it.

It’s not ideal, and it’s slower than direct calls, but you can use Java’s reflection to outcome this, while you wait for the Scala fix:

  classOf[SomeClass].getMethod("someMethod", classOf[String]).invoke(this, "Some arg")

This way you explicitly define the method, specifying its parameters, that you want to call.

scala

Page 1 of 2
Next