Play async example resulting in BeanSerializer issues

208 views
Skip to first unread message

Alex Mueller

unread,
Feb 23, 2016, 11:30:17 AM2/23/16
to play-framework
My question/issue is two-fold. One is the actual error I am getting. The second is the approach I am taking with my controller, service, dao, and entity as far as pushing the responsibility of @Transactional to my service and not on my controller method.

Here is my code. I am just showing the parts I think are relevant. I do not have anything in regards to Jackson in my sbt files. The errors I am getting are below the code.

// Here is my controller (below), I have removed @Transactional(readOnly=true)
// to push this to my service.

@Inject
public UserController(UserService userService){
    this.userService = userService;
}

public Result jsonResult(Result httpResponse) {
    response().setContentType("application/json; charset=utf-8");
    return httpResponse;
}

public Result list(int page, int size) {
    F.Promise<User> user = userService.findByUserNameAsync("John");
    return jsonResult(ok(Json.toJson(user)));
}

// The controller calls my service, as shown below. I am trying to use an
// async approach.

@Inject
public UserService(UserDao dao){
    this.dao = dao;
}

public F.Promise<User> findByUserNameAsync(String userName){
    return F.Promise.promise(() -> JPA.withTransaction(() -> {
        return dao.findByUserName(userName);
    }));

// This implementation fails with the same serialization error.
//        return F.Promise.promise(new F.Function0<User>() {
//            @Override
//            public User apply() throws Throwable {
//                return dao.findByUserName(userName);
//            }
//        });
}

// The service calls my dao, shown below. This is synchronous and blocking.

public User findByUserName(String userName) {
    return JPA.em()
        .createNamedQuery("User.findByUserName", User.class)
        .setParameter("userName", userName)
        .getSingleResult();
}

// My User class, below. I use @JsonIgnore on List<?> fields. All fields are
// public, which should provider internal getters/setters.

@Entity
@Table(name="user")
@NamedQueries({
    @NamedQuery(name="User.findByUserName",
        query="SELECT u FROM User u WHERE u.userName = :userName")
})
public class User {
    @Id
    @GeneratedValue(strategy= GenerationType.AUTO)
    @Column(name="id", nullable=false)
    public Long id;

    @Column(name="user_name", nullable=false, length=255)
    public String userName;

    @Column(name="first_name", nullable=true, length=255)
    public String firstName;

    @Column(name="last_name", nullable=true, length=255)
    public String lastName;

    @Column(name="password_hash", nullable=false, length=255)
    public String passwordHash;

    @Column(name="password_salt", nullable=false, length=255)
    public String passwordSalt;

    @Column(name="email", nullable=false, length=255)
    public String email;

    @JsonIgnore
    @ManyToMany(cascade = CascadeType.ALL)
    @JoinTable(name = "user_role",
        joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
        inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"))
    public List<Role> roles;

    @JsonIgnore
    @ManyToMany(cascade = CascadeType.ALL)
    @JoinTable(name = "user_permission",
        joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
        inverseJoinColumns = @JoinColumn(name = "permission_id", referencedColumnName = "id"))
    public List<Permission> permissions;
}

Below is the error message.

RuntimeException: java.lang.IllegalArgumentException: No serializer found for class play.libs.F$Promise and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) )]

[error] application - 

! @6p818jfcg - Internal server error, for (GET) [/users] ->
 
play.api.http.HttpErrorHandlerExceptions$$anon$1: Execution exception[[RuntimeException: java.lang.IllegalArgumentException: No serializer found for class play.libs.F$Promise and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) )]]
at play.api.http.HttpErrorHandlerExceptions$.throwableToUsefulException(HttpErrorHandler.scala:265) ~[play_2.11-2.4.6.jar:2.4.6]
at play.api.http.DefaultHttpErrorHandler.onServerError(HttpErrorHandler.scala:191) ~[play_2.11-2.4.6.jar:2.4.6]
at play.api.GlobalSettings$class.onError(GlobalSettings.scala:179) [play_2.11-2.4.6.jar:2.4.6]
at play.api.DefaultGlobal$.onError(GlobalSettings.scala:212) [play_2.11-2.4.6.jar:2.4.6]
at play.api.http.GlobalSettingsHttpErrorHandler.onServerError(HttpErrorHandler.scala:94) [play_2.11-2.4.6.jar:2.4.6]
at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$9$$anonfun$apply$1.applyOrElse(PlayDefaultUpstreamHandler.scala:151) [play-netty-server_2.11-2.4.6.jar:2.4.6]
at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$9$$anonfun$apply$1.applyOrElse(PlayDefaultUpstreamHandler.scala:148) [play-netty-server_2.11-2.4.6.jar:2.4.6]
at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:36) [scala-library-2.11.7.jar:na]
at scala.util.Failure$$anonfun$recover$1.apply(Try.scala:216) [scala-library-2.11.7.jar:na]
at scala.util.Try$.apply(Try.scala:192) [scala-library-2.11.7.jar:na]
Caused by: java.lang.RuntimeException: java.lang.IllegalArgumentException: No serializer found for class play.libs.F$Promise and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) )
at play.libs.Json.toJson(Json.java:78) ~[play-json_2.11-2.4.6.jar:2.4.6]
at controllers.UserController.list(UserController.java:45) ~[classes/:na]
at router.Routes$$anonfun$routes$1$$anonfun$applyOrElse$6$$anonfun$apply$6.apply(Routes.scala:223) ~[classes/:na]
at router.Routes$$anonfun$routes$1$$anonfun$applyOrElse$6$$anonfun$apply$6.apply(Routes.scala:223) ~[classes/:na]
at play.core.routing.HandlerInvokerFactory$$anon$4.resultCall(HandlerInvoker.scala:136) ~[play_2.11-2.4.6.jar:2.4.6]
at play.core.routing.HandlerInvokerFactory$JavaActionInvokerFactory$$anon$14$$anon$3$$anon$1.invocation(HandlerInvoker.scala:127) ~[play_2.11-2.4.6.jar:2.4.6]
at play.core.j.JavaAction$$anon$1.call(JavaAction.scala:70) ~[play_2.11-2.4.6.jar:2.4.6]
at play.http.DefaultHttpRequestHandler$1.call(DefaultHttpRequestHandler.java:20) ~[play_2.11-2.4.6.jar:2.4.6]
at play.core.j.JavaAction$$anonfun$7.apply(JavaAction.scala:94) ~[play_2.11-2.4.6.jar:2.4.6]
at play.core.j.JavaAction$$anonfun$7.apply(JavaAction.scala:94) ~[play_2.11-2.4.6.jar:2.4.6]
Caused by: java.lang.IllegalArgumentException: No serializer found for class play.libs.F$Promise and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) )
at com.fasterxml.jackson.databind.ObjectMapper.valueToTree(ObjectMapper.java:2374) ~[jackson-databind-2.5.4.jar:2.5.4]
at play.libs.Json.toJson(Json.java:76) ~[play-json_2.11-2.4.6.jar:2.4.6]
at controllers.UserController.list(UserController.java:45) ~[classes/:na]
at router.Routes$$anonfun$routes$1$$anonfun$applyOrElse$6$$anonfun$apply$6.apply(Routes.scala:223) ~[classes/:na]
at router.Routes$$anonfun$routes$1$$anonfun$applyOrElse$6$$anonfun$apply$6.apply(Routes.scala:223) ~[classes/:na]
at play.core.routing.HandlerInvokerFactory$$anon$4.resultCall(HandlerInvoker.scala:136) ~[play_2.11-2.4.6.jar:2.4.6]
at play.core.routing.HandlerInvokerFactory$JavaActionInvokerFactory$$anon$14$$anon$3$$anon$1.invocation(HandlerInvoker.scala:127) ~[play_2.11-2.4.6.jar:2.4.6]
at play.core.j.JavaAction$$anon$1.call(JavaAction.scala:70) ~[play_2.11-2.4.6.jar:2.4.6]
at play.http.DefaultHttpRequestHandler$1.call(DefaultHttpRequestHandler.java:20) ~[play_2.11-2.4.6.jar:2.4.6]
at play.core.j.JavaAction$$anonfun$7.apply(JavaAction.scala:94) ~[play_2.11-2.4.6.jar:2.4.6]
Caused by: com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class play.libs.F$Promise and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) )
at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:59) ~[jackson-databind-2.5.4.jar:2.5.4]
at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.serialize(UnknownSerializer.java:26) ~[jackson-databind-2.5.4.jar:2.5.4]
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:129) ~[jackson-databind-2.5.4.jar:2.5.4]
at com.fasterxml.jackson.databind.ObjectMapper.writeValue(ObjectMapper.java:2242) ~[jackson-databind-2.5.4.jar:2.5.4]
at com.fasterxml.jackson.databind.ObjectMapper.valueToTree(ObjectMapper.java:2369) ~[jackson-databind-2.5.4.jar:2.5.4]
at play.libs.Json.toJson(Json.java:76) ~[play-json_2.11-2.4.6.jar:2.4.6]
at controllers.UserController.list(UserController.java:45) ~[classes/:na]
at router.Routes$$anonfun$routes$1$$anonfun$applyOrElse$6$$anonfun$apply$6.apply(Routes.scala:223) ~[classes/:na]
at router.Routes$$anonfun$routes$1$$anonfun$applyOrElse$6$$anonfun$apply$6.apply(Routes.scala:223) ~[classes/:na]
at play.core.routing.HandlerInvokerFactory$$anon$4.resultCall(HandlerInvoker.scala:136) ~[play_2.11-2.4.6.jar:2.4.6]


I am not sure where I would need to add Jackson ObjectMapper setVisibility updates, as I have seen in some answers.

Thanks for any help.

Alex Mueller

unread,
Feb 24, 2016, 11:12:57 AM2/24/16
to play-framework
I believe the reason for my JSON serialization error is that due to the async nature of the call, my controller will return before my service calls my dao to get the data. This means, in my controller, I am serializing a User object that is a future and not a real User object yet (as the service has not called the dao to return the valid User). Maybe I am doing this all wrong. 

Maybe my real question is my second question in the original post dealing with my approach. In this REST api I am creating, does it make sense to use F.Promise to wrap up my server and dao method calls if my controller is going to return before my service and dao return? Should I not do this? Is there some async performance I am missing out on if I am not using F.Promise? I am associating async with, "your api will be faster, you need to use it, don't block any calls."

Thanks.

Alex Mueller

unread,
Feb 25, 2016, 12:59:23 PM2/25/16
to play-framework
The error in my scenario is that I am failing to map my object to my return promise. Once I do this, my JSON serialization issues go away.

public F.Promise<Result> list(int page, int size) {

    F.Promise<User> user = userService.findByUserNameAsync("John");

    F.Promise<Result> resultPromise = user.map(
        u -> jsonResult(ok(Json.toJson(u)))
    );

    return resultPromise;
}


Reply all
Reply to author
Forward
0 new messages