Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Since version 2.9.0, APIs with @PathVariable are not picked up anymore due to Spring 4.3.3 code #2511

Closed
dbaje opened this issue Jun 25, 2018 · 1 comment

Comments

@dbaje
Copy link
Contributor

dbaje commented Jun 25, 2018

In our project we are currently using spring 4.2.X and we recently upgraded to version 2.9.0 of this library. After upgrading we discovered that some of our APIs didn't appear anymore. After some digging, we discovered that an error was thrown and then caught which made troubleshooting difficult.

The issue is in the ParameterRequiredReader class

It's using fields in the PathVariable annotation that was introduced in Spring 4.3.3. Using a lower Spring version triggers a "MethodNotFound" exception. In our case, upgrading Spring is not an option.

As a work around, we have extended the offending class, modified the code. Then we told Spring to use ours as Primary. So we got it currently working.

There's a way for the code to do the same thing but without breaking compatibility

This:

Optional<PathVariable> pathVariable = methodParameter.findAnnotation(PathVariable.class);
    if (pathVariable.isPresent()) {
      String paramName = MoreObjects.firstNonNull(
          emptyToNull(pathVariable.get().name()),
          methodParameter.defaultName().orNull());
      if (pathVariable.get().required() ||
          optionalButPresentInThePath(operationContext, pathVariable.get(), paramName)) {
        requiredSet.add(true);
      }
    }

Could become this:

Optional<PathVariable> pathVariable = methodParameter.findAnnotation(PathVariable.class);
    if (pathVariable.isPresent()) {
      Map<String, Object> attributes = AnnotationUtils.getAnnotationAttributes(pathVariable.get());
      String paramName = MoreObjects.firstNonNull(
          emptyToNull((String) attributes.get("name")),
          methodParameter.defaultName().orNull());

      boolean required = !attributes.containsKey("required") || (Boolean) attributes.get("required");
      if (required ||
          optionalButPresentInThePath(operationContext, paramName)) {
        requiredSet.add(true);
      }
    }

Then this:

private boolean optionalButPresentInThePath(
      OperationContext operationContext,
      PathVariable pathVariable,
      String paramName) {

    return !pathVariable.required()
         && operationContext.requestMappingPattern().contains("{" + paramName + "}");
  }

To This:

private boolean optionalButPresentInThePath(
          OperationContext operationContext,
          String paramName) {

    return operationContext.requestMappingPattern().contains("{" + paramName + "}");
  }

AnnotationUtils is present for Spring since early version and certainly present in all of 4.X versions. That utility class contains a method to get all annotation attributes as a Map. In this case, it's useful to make some annotation changes backwards compatible like in this case.

@dilipkrish
Copy link
Member

Happy to take a PR for that. You can use the convenience method offered via the spring capabilities utility class

@dbaje dbaje mentioned this issue Jun 25, 2018
@dilipkrish dilipkrish added this to the 3.0 milestone Jul 13, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants