Skip to content

ClassNotFoundException: DefaultedPageable #729

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

Closed
ivangfr opened this issue Jun 13, 2020 · 10 comments
Closed

ClassNotFoundException: DefaultedPageable #729

ivangfr opened this issue Jun 13, 2020 · 10 comments
Labels
bug Something isn't working

Comments

@ivangfr
Copy link

ivangfr commented Jun 13, 2020

Hi, I have recently migrated my springboot (version 2.3.1) app from springfox to springdoc openapi (version 1.4.1).

The app uses spring-boot-starter-data-elasticsearch and has some endpoints that receive Pageable as parameters and return a Page.

I've added those 2 dependencies

<!-- SpringDoc OpenApi -->
<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-ui</artifactId>
    <version>1.4.1</version>
</dependency>
<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-data-rest</artifactId>
    <version>1.4.1</version>
</dependency>

I am getting an exception whose stacktrace is shown below. It's complaining that org.springframework.data.rest.webmvc.support.DefaultedPageable is missing.

2020-06-13 20:14:32.508 ERROR [publisher-api,,,] 33952 --- [           main] o.s.boot.SpringApplication               : Application run failed

java.lang.IllegalStateException: Unable to load cache item
        at org.springframework.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:79) ~[spring-core-5.2.7.RELEASE.jar:5.2.7.RELEASE]
        at org.springframework.cglib.core.internal.LoadingCache.get(LoadingCache.java:34) ~[spring-core-5.2.7.RELEASE.jar:5.2.7.RELEASE]
        at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:134) ~[spring-core-5.2.7.RELEASE.jar:5.2.7.RELEASE]
        at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:319) ~[spring-core-5.2.7.RELEASE.jar:5.2.7.RELEASE]
        at org.springframework.cglib.proxy.Enhancer.createHelper(Enhancer.java:572) ~[spring-core-5.2.7.RELEASE.jar:5.2.7.RELEASE]
        at org.springframework.cglib.proxy.Enhancer.createClass(Enhancer.java:419) ~[spring-core-5.2.7.RELEASE.jar:5.2.7.RELEASE]
        at org.springframework.context.annotation.ConfigurationClassEnhancer.createClass(ConfigurationClassEnhancer.java:137) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
        at org.springframework.context.annotation.ConfigurationClassEnhancer.enhance(ConfigurationClassEnhancer.java:109) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
        at org.springframework.context.annotation.ConfigurationClassPostProcessor.enhanceConfigurationClasses(ConfigurationClassPostProcessor.java:423) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
        at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanFactory(ConfigurationClassPostProcessor.java:257) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
        at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:291) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
        at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:131) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
        at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:707) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:533) ~[spring-context-5.2.7.RELEASE.jar:5.2.7.RELEASE]
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:143) ~[spring-boot-2.3.1.RELEASE.jar:2.3.1.RELEASE]
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758) ~[spring-boot-2.3.1.RELEASE.jar:2.3.1.RELEASE]
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:750) ~[spring-boot-2.3.1.RELEASE.jar:2.3.1.RELEASE]
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) ~[spring-boot-2.3.1.RELEASE.jar:2.3.1.RELEASE]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.3.1.RELEASE.jar:2.3.1.RELEASE]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1237) ~[spring-boot-2.3.1.RELEASE.jar:2.3.1.RELEASE]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) ~[spring-boot-2.3.1.RELEASE.jar:2.3.1.RELEASE]
        at com.mycompany.publisherapi.PublisherApiApplication.main(PublisherApiApplication.java:12) ~[classes/:na]
Caused by: java.lang.NoClassDefFoundError: org/springframework/data/rest/webmvc/support/DefaultedPageable
        at org.springdoc.data.rest.SpringDocDataRestConfiguration.<clinit>(SpringDocDataRestConfiguration.java:74) ~[springdoc-openapi-data-rest-1.4.1.jar:1.4.1]
        at java.base/java.lang.Class.forName0(Native Method) ~[na:na]
        at java.base/java.lang.Class.forName(Class.java:398) ~[na:na]
        at org.springframework.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:571) ~[spring-core-5.2.7.RELEASE.jar:5.2.7.RELEASE]
        at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:363) ~[spring-core-5.2.7.RELEASE.jar:5.2.7.RELEASE]
        at org.springframework.cglib.proxy.Enhancer.generate(Enhancer.java:585) ~[spring-core-5.2.7.RELEASE.jar:5.2.7.RELEASE]
        at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:110) ~[spring-core-5.2.7.RELEASE.jar:5.2.7.RELEASE]
        at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData$3.apply(AbstractClassGenerator.java:108) ~[spring-core-5.2.7.RELEASE.jar:5.2.7.RELEASE]
        at org.springframework.cglib.core.internal.LoadingCache$2.call(LoadingCache.java:54) ~[spring-core-5.2.7.RELEASE.jar:5.2.7.RELEASE]
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na]
        at org.springframework.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:61) ~[spring-core-5.2.7.RELEASE.jar:5.2.7.RELEASE]
        ... 21 common frames omitted
Caused by: java.lang.ClassNotFoundException: org.springframework.data.rest.webmvc.support.DefaultedPageable
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581) ~[na:na]
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178) ~[na:na]
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521) ~[na:na]
        ... 32 common frames omitted

Thanks in advance!

@verdan-zz
Copy link

verdan-zz commented Jun 15, 2020

Find class SpringDocDataRestConfiguration and overide it.
In my case it occurs that I haven't got mvc dependency, when I added it many wrong things happend :)
So I created in my code (Yes I know that this is workaround)

package org.springdoc.data.rest;

import com.querydsl.core.types.Predicate;
import org.springdoc.core.converters.models.Pageable;
import org.springdoc.data.rest.customisers.QuerydslPredicateOperationCustomizer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import org.springframework.data.querydsl.binding.QuerydslBindingsFactory;
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;

import java.util.Optional;

import static org.springdoc.core.Constants.SPRINGDOC_ENABLED;
import static org.springdoc.core.SpringDocUtils.getConfig;

@configuration
@ConditionalOnProperty(name = SPRINGDOC_ENABLED, matchIfMissing = true)
public class SpringDocDataRestConfiguration {

static {
    getConfig().replaceWithClass(org.springframework.data.domain.Pageable.class, Pageable.class)
            .replaceWithClass(org.springframework.data.domain.PageRequest.class, Pageable.class);
}

@Bean
@ConditionalOnMissingBean
@Primary
@Lazy(false)
DataRestHalProvider halProvider(Optional<RepositoryRestConfiguration> repositoryRestConfiguration) {
    return new DataRestHalProvider(repositoryRestConfiguration);
}


@ConditionalOnClass(value = {QuerydslBindingsFactory.class})
class QuerydslProvider {

    @Bean
    @ConditionalOnMissingBean
    @Lazy(false)
    QuerydslPredicateOperationCustomizer queryDslQuerydslPredicateOperationCustomizer(Optional<QuerydslBindingsFactory> querydslBindingsFactory) {
        if (querydslBindingsFactory.isPresent()) {
            getConfig().addRequestWrapperToIgnore(Predicate.class);
            return new QuerydslPredicateOperationCustomizer(querydslBindingsFactory.get());
        }
        return null;
    }
}

}

@OskarsPakers
Copy link

Adding dependency spring-boot-starter-data-rest seem to resolve the error.

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>

Would be nice if springdoc would work without this dependency too.

@bnasslahsen
Copy link
Collaborator

@OskarsPakers,

From your description, you don't need to load springdoc-openapi-data-rest. (You will load unecessary beans related to spring-data-rest)

If you just need to enable the support of Pageable, you can just add the following line:

SpringDocUtils.getConfig().replaceWithClass(org.springframework.data.domain.Pageable.class, Pageable.class);

This is explained here: https://springdoc.org/
Section [Spring Data Rest support]

Also, a fix will be added to handle this case, for projects relying on springdoc-openapi-data-rest for Pageable Support.

@ivangfr
Copy link
Author

ivangfr commented Jun 18, 2020

Hi @bnasslahsen , thanks for solving the issue!

What I did was, basically, remove the springdoc-openapi-data-rest dependency and add the following to my SpringDocOpenApiConfig file:

@Configuration
public class SpringDocOpenApiConfig {

	...

    static {
        SpringDocUtils.getConfig().replaceWithClass(org.springframework.data.domain.Pageable.class, Pageable.class);
    }

}

Besides, in the controller endpoint that uses Pageable, I hidded the pageable parameter (because I didn't want it as request body) and added the @PageableAsQueryParam annotation

@GetMapping
@PageableAsQueryParam
public Page<News> getNews(@Parameter(hidden = true) Pageable pageable) {
    return newsService.listAllNewsByPage(pageable);
}

So far so good!

But, just one question: in the documentation, it stated that

"Since, v1.3.1 you can use as well @ParameterObject instead of @PageableAsQueryParam for HTTP GET methods."

Do you have an example that uses @ParameterObject. I was not able to set it.

Thanks!

@bnasslahsen
Copy link
Collaborator

@ivangfr
Copy link
Author

ivangfr commented Jun 18, 2020

Hey @bnasslahsen ,

After checking your example, I was able to use @ParameterObject to handle the Pageable.

However, in order to make it works, I needed to add back the dependency org.springdoc:springdoc-openapi-data-rest:1.4.1. And by adding this dependency, in order to not have the ClassNotFoundException exceptions that I described in the issue, I need also to add org.springframework.boot:spring-boot-starter-data-rest as suggested by @OskarsPakers

P.S. I still have SpringDocUtils.getConfig().replaceWithClass(org.springframework.data.domain.Pageable.class, Pageable.class);

But, I can stick to @PageableAsQueryParam, no problem. Thanks

@bnasslahsen
Copy link
Collaborator

@ivangfr,

@ParameterObject is completly independent from org.springdoc:springdoc-openapi-data-rest:1.4.1.

Why did you need back org.springdoc:springdoc-openapi-data-rest ?

@ivangfr
Copy link
Author

ivangfr commented Jun 18, 2020

Here is my project: https://github.com/ivangfr/spring-cloud-stream-elasticsearch/tree/master/publisher-api

In the pom.xml, I have

<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-ui</artifactId>
    <version>1.4.1</version>
</dependency>

My StringDocOpenApiConfig.java

@Configuration
public class SpringDocOpenApiConfig {

    ...

    static {
        SpringDocUtils.getConfig().replaceWithClass(org.springframework.data.domain.Pageable.class, Pageable.class);
    }

}

My enpoint

@Operation(summary = "Get News")
@PageableAsQueryParam
@GetMapping
public Page<News> getNews(@Parameter(hidden = true) Pageable pageable) {
    return newsService.listAllNewsByPage(pageable);
}

/v3/api-docs

{
    ...
    "paths": {
        "/api/news": {
            "get": {
                "operationId": "getNews",
                "parameters": [
                    {
                        "description": "Zero-based page index (0..N)",
                        "in": "query",
                        "name": "page",
                        "schema": {
                            "default": "0",
                            "type": "integer"
                        }
                    },
                    {
                        "description": "The size of the page to be returned",
                        "in": "query",
                        "name": "size",
                        "schema": {
                            "default": "20",
                            "type": "integer"
                        }
                    },
                    {
                        "description": "Sorting criteria in the format: property(,asc|desc). Default sort order is ascending. Multiple sort criteria are supported.",
                        "in": "query",
                        "name": "sort",
                        "schema": {
                            "items": {
                                "type": "string"
                            },
                            "type": "array"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "content": {
                            "*/*": {
                                "schema": {
                                    "$ref": "#/components/schemas/PageNews"
                                }
                            }
                        },
                        "description": "OK"
                    }
                },
                "summary": "Get News",
                "tags": [
                    "news-controller"
                ]
            }
        }
    },
    ...
}

It doesn't work if I change the endpoint configuration to

import io.swagger.v3.oas.annotations.Operation;
import org.springdoc.api.annotations.ParameterObject;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import javax.validation.constraints.NotNull;
...

@Operation(summary = "Get News")
@GetMapping
public Page<News> getNews(@NotNull @ParameterObject Pageable pageable) {
    return newsService.listAllNewsByPage(pageable);
}

/v3/api-docs

{
    ...
    "paths": {
        "/api/news": {
            "get": {
                "operationId": "getNews",
                "responses": {
                    "200": {
                        "content": {
                            "*/*": {
                                "schema": {
                                    "$ref": "#/components/schemas/PageNews"
                                }
                            }
                        },
                        "description": "OK"
                    }
                },
                "summary": "Get News",
                "tags": [
                    "news-controller"
                ]
            }
        }
    },
    ...
}

@bnasslahsen
Copy link
Collaborator

bnasslahsen commented Jun 18, 2020

@ivangfr,

It's working on your sample as well, using @ParameterObject!
I am quite sure you didn't import the right Pageable type.
For less confusion, here is the right working code:

    static {
        SpringDocUtils.getConfig().replaceWithClass(org.springframework.data.domain.Pageable.class, org.springdoc.core.converters.models.Pageable.class);
    }

Let me know, if you made it work, so we can update the documentation accordingly.

@ivangfr
Copy link
Author

ivangfr commented Jun 18, 2020

Hey @bnasslahsen ! Exactly, that was the problem!

I've imported from org.springframework.data.domain.Pageable instead of org.springdoc.core.converters.models.

By setting the Pageable absolute class name in the 2nd argument of replaceWithClass method will avoid some confusion.

Best

@bnasslahsen bnasslahsen added the bug Something isn't working label Jan 10, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants