Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jersey JAX-RS REST "getter" method always called

Tags:

I am running a web application in Glasshfish 5 that provides a REST endpoint using Jersey JAX-RS. It also uses bean validation. The problem I am having is that any method starting with "get" is always called when it returns something that has the @Valid annotation.

Example:

@Path("/hello")
public class HelloResource {

  @GET
  public @Valid HelloMessage getSomething() {
    HelloMessage helloMessage = new HelloMessage();
    helloMessage.setMessage("Hello World!");
    return helloMessage;
  }

  @POST
  public @Valid HelloMessage updateMessage(@Valid HelloMessage message) {
    return message;
  }
}

If I do a POST to /hello, you will see the getSomething method being called, before updateMessage is called. If I remove the @Valid annotation on the return type of the getSomething method, then getSomething is not called.

Is this according to specifications? Should you basically never name a method starting with "get" in a REST class?

In the past I have reported an issue for this on github, but never received a reply.

https://github.com/eclipse-ee4j/jersey/issues/3743

Other classes:

@ApplicationPath("/")
public class HelloApplication extends Application {

  @Override
  public Set<Class<?>> getClasses() {
    return Collections.singleton(HelloResource.class);
  }
}

public class HelloMessage {

  @Size(max = 100)
  private String message;

  public String getMessage() {
    return message;
  }

  public void setMessage(String message) {
    this.message = message;
  }
}

Minimal project to be found at https://github.com/robertatgh/stackoverflow-50658396/tree/develop

like image 911
Robert Avatar asked Jun 02 '18 15:06

Robert


1 Answers

So this turned out to be a funny kind of an issue because of the naming convention. Debugging through the jersey source code you will see that it goes through

org.glassfish.jersey.server.validation.internal.DefaultConfiguredValidator.onValidate(ValidationInterceptorContext) line: 166

public void onValidate(final ValidationInterceptorContext ctx) {

    final Object resource = ctx.getResource();
    final Invocable resourceMethod = ctx.getInvocable();
    final Object[] args = ctx.getArgs();

    final Set<ConstraintViolation<Object>> constraintViolations = new HashSet<>();
    final BeanDescriptor beanDescriptor = getConstraintsForClass(resource.getClass());

    // Resource validation.
    if (beanDescriptor.isBeanConstrained()) {
        constraintViolations.addAll(validate(resource));
    }

    if (resourceMethod != null
            && configuration.getBootstrapConfiguration().isExecutableValidationEnabled()) {
        final Method handlingMethod = resourceMethod.getHandlingMethod();

The interesting part is around

// Resource validation.
if (beanDescriptor.isBeanConstrained()) {
    constraintViolations.addAll(validate(resource));
}

The definition of the same is

@Override
public final boolean isBeanConstrained() {
    return hasConstraints() || !constrainedProperties.isEmpty();
}

Now if you look at the value of constrainedProperties it shows below

getSomething is a property

So it thinks that getSomething means a property something which then inserts a validation on the property itself.

So now if we rename the method like below

  @GET
  public @Valid HelloMessage doGetSomething() {
    System.out.println("* * * *---==** getSomething() called **==---* * * *");
    HelloMessage helloMessage = new HelloMessage();
    helloMessage.setMessage("H");
    return helloMessage;
  }

  @POST
  public  @Valid HelloMessage updateMessage(@Valid HelloMessage message) {
      message.setMessage("H");
    System.out.println("* * * *---==** updateMessage() called **==---* * * *");

    return message;
  }

And run it again from command line

getSomething is not Called

And of course if I correct the return value with valid data

getSomething not called

like image 199
Tarun Lalwani Avatar answered Oct 04 '22 16:10

Tarun Lalwani