2

I am building an application using Google App Engine, Jersey, Objectify, and Gson (and some other tiny library).

I would like to do validation in the resource using the @Valid annotation. However, no actual validation seems to be performed. In particular, when posting an Entry with payee set to "123" to both /entries and /entries/nocheck, the app does not raise any exception and the entity is being saved in the datastore.

Here is a snippet of the entity:

@Entity
public class Entry {

  @Id
  private Long id;
  private LocalDate date;
  @com.sappenin.objectify.annotation.Money
  private Money amount;
  @NotNull @Pattern(regexp = "[a-zA-Z][a-zA-Z0-9]*") @Length(min = 5)
  private String payee;
  private String description;
  private String note;

  //...
}

Here is a snippet of the resource:

@Path("/entries")
@Produces(MediaType.APPLICATION_JSON)
public class TestApi {

  @POST
  @Consumes(MediaType.APPLICATION_JSON)
  public Response create(@Valid Entry entry) {
    OfyService.ofy().save().entities(entry).now();
    return Response.ok(entry).build();
  }

  @POST
  @Path("/nocheck")
  @Consumes(MediaType.APPLICATION_JSON)
  public Response createNoCheck(Entry entry) {
    OfyService.ofy().save().entities(entry).now();
    return Response.ok(entry).build();
  }

}

Here is the application:

public class MyApplication extends ResourceConfig {

  public MyApplication() {
    packages(
            "it.newfammulfin.model",
            "it.newfammulfin.api",
            "it.newfammulfin.api.util");
  }

}

Note that the pom.xml includes this dependency:

<dependency>
  <groupId>org.glassfish.jersey.ext</groupId>
  <artifactId>jersey-bean-validation</artifactId>
  <version>2.22.1</version>
</dependency>

which should cause, due to the auto-discovery feature, the validation to be enabled without further configuration.

What am I missing?

Update

I added a method in the resource which explicitly validates the entity:

  @POST
  @Path("validate")
  @Consumes(MediaType.APPLICATION_JSON)
  public Response testValidate(@Valid Entry entry) {
    ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
    Validator validator = factory.getValidator();
    System.out.println("validator is "+validator.getClass().getName());
    System.out.println(validator.validate(entry));
    return Response.ok(entry).build();
  }

The validation through the invocation of validate() works. Yet, the validation before the actual invocation of testValidate() does not. As an aside, I had to use the Apache BVal implementation of the JSR303 Bean Validation, because of some issues between Hibernate Validator and Google App Engine.

Community
  • 1
  • 1
Eric
  • 321
  • 3
  • 9
  • 3
    I confirm: mentioned `jersey-bean-validation` dependency is crucial. If I were you, I would double check if I'm really using Jersey 2.x (because support for validation has been added since 2.x). – G. Demecki Oct 16 '15 at 08:38
  • Yes, I read about version issues. I am using Jersey 2.2. – Eric Oct 16 '15 at 12:31

2 Answers2

3

In jersey 2.X you have to register your extensions. However validation seems to be the exception, as described here. I'd try it anyway and see what you can configure with:

  • CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE
  • ServerProperties.FEATURE_AUTO_DISCOVERY_DISABLE
  • ServerProperties.BV_FEATURE_DISABLE

Your application class could look like this:

public class MyApplication extends ResourceConfig {
  public MyApplication() {
    register(ValidationFeature.class);
    property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
    property(ServerProperties.BV_DISABLE_VALIDATE_ON_EXECUTABLE_OVERRIDE_CHECK, true)
    packages(
            "it.newfammulfin.model",
            "it.newfammulfin.api",
            "it.newfammulfin.api.util");
  }
}
konqi
  • 5,137
  • 3
  • 34
  • 52
  • Your proposed solution indeed moved something, but did not actually solve my case. First, I got a `ClassNotFoundException` which I solved by adding the `jersey-guava` maven dependency. Now I see a `NoSuchMethodError` complaining about the method `javax.validation.Configuration.getBootstrapConfiguration()`. – Eric Oct 16 '15 at 12:35
  • 1
    hm please add all your jersey related dependencies from your pom.xml. This certainly looks like a missing dependency. – konqi Oct 16 '15 at 13:03
  • 1
    The full project is on [github](https://github.com/ericmedvet/fammulfin), here is the [pom.xml](https://github.com/ericmedvet/fammulfin/blob/226e7370a7237ff1c0c0ac1a1272c42c558db6e6/pom.xml) (the link points at the current version, for future reference). – Eric Oct 16 '15 at 13:09
  • I have the following additionally: org.glassfish.jersey.core.jersey-client, org.glassfish.jersey.bundles.jaxrs-ri - The latter sounds promising. – konqi Oct 16 '15 at 13:21
  • Another tiny step ahead, but still unsolved: `failed Jersey: java.lang.NoSuchMethodError: org.glassfish.hk2.utilities.reflection.ReflectionHelper.getScopeFromClass()`. – Eric Oct 16 '15 at 14:14
  • hk2 is a dependency injector that should come as a dependency to jersey. Manually add this: http://mvnrepository.com/artifact/org.glassfish.hk2/hk2-utils I'll check how the dependency resolves in my project. Edit: HK2 comes with the jersey-client in my project. – konqi Oct 16 '15 at 14:48
  • The fact that I am dealing with a `NoSuchMethodException` rather than with a `ClassNotFoundException` makes me think that there might be a component with wrong version rather than a missing dependency. Don't you? Anyway, I'll try. – Eric Oct 16 '15 at 15:20
  • 1
    True. So try `mvn dependency:tree` and see whether and why a "bad version" got in. – konqi Oct 16 '15 at 15:27
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/92544/discussion-between-eric-and-konqi). – Eric Oct 16 '15 at 21:03
3

I solved the problem by carefully cleaning my pom.xml and updating all versions. Correct dependencies are as follows (I am using 1.9.27 version of Google App Engine):

<dependency>
    <groupId>com.google.appengine</groupId>
    <artifactId>appengine-api-1.0-sdk</artifactId>
    <version>${appengine.version}</version>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>2.5</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.containers</groupId>
    <artifactId>jersey-container-servlet-core</artifactId>
    <version>2.22.1</version>
</dependency>
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.4</version>
</dependency>
<dependency>
    <groupId>com.googlecode.objectify</groupId>
    <artifactId>objectify</artifactId>
    <version>5.1.8</version>
</dependency>
<dependency>
    <groupId>org.joda</groupId>
    <artifactId>joda-money</artifactId>
    <version>0.10.0</version>
</dependency>
<dependency>
    <groupId>joda-time</groupId>
    <artifactId>joda-time</artifactId>
    <version>2.8.2</version>
</dependency>
<dependency>
    <groupId>com.sappenin.objectify</groupId>
    <artifactId>objectify-utils</artifactId>
    <version>5.1.3</version>
</dependency>
<dependency>
    <groupId>com.fatboyindustrial.gson-jodatime-serialisers</groupId>
    <artifactId>gson-jodatime-serialisers</artifactId>
    <version>1.2.0</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.ext</groupId>
    <artifactId>jersey-bean-validation</artifactId>
    <version>2.22.1</version>
</dependency>
<dependency>
    <groupId>de.odysseus.juel</groupId>
    <artifactId>juel-api</artifactId>
    <version>2.2.7</version>
</dependency>
<dependency>
    <groupId>de.odysseus.juel</groupId>
    <artifactId>juel-impl</artifactId>
    <version>2.2.7</version>
</dependency>

Note that the last two, concerning juel, were needed to avoid this problem (see the comment concerning the message parameter): see also this related solved issue concerning an incompatibility between Google App Engine and juel.

Community
  • 1
  • 1
Eric
  • 321
  • 3
  • 9