microstream-one / microstream

High-Performance Java-Native-Persistence. Store and load any Java Object Graph or Subgraphs partially, Relieved of Heavy-weight JPA. Microsecond Response Time. Ultra-High Throughput. Minimum of Latencies. Create Ultra-Fast In-Memory Database Applications & Microservices.
https://microstream.one/
Eclipse Public License 2.0
558 stars 44 forks source link

error using the Spark/Jetty REST API: NoClassDefFoundError: org/eclipse/jetty/server/session/SessionHandler #695

Closed dominik42 closed 7 months ago

dominik42 commented 7 months ago

Environment Details

Describe the bug

I want to use the REST Client GUI in order to introspect the Microstream storage within my Vaadin/SpringBoot App. Prio the necessary changes, the Vaadin App starts sucessfully aand microstream works as expected. Then I follow the docs and start setup the REST API using the default configuration:

1) add dependencies

    <dependency>
        <groupId>one.microstream</groupId>
        <artifactId>microstream-integrations-spring-boot</artifactId>
        <version>08.01.01-MS-GA</version>
    </dependency>
    <dependency>
        <groupId>one.microstream</groupId>
        <artifactId>microstream-storage-restservice-sparkjava</artifactId>
        <version>08.01.01-MS-GA</version>
    </dependency>

2) enabling the REST API (BTW: please mention in the docs, that a separate thread is ncessary at least for spring boot apps; without that, the call of service.start() doesn't return)

   StorageRestService service = StorageRestServiceResolver.resolve(storageManager);
    // and start it
    Runnable restAPI = new Runnable() {
        @Override
        public void run() {
            service.start();
        }
    };

    ExecutorService executorService = Executors.newSingleThreadExecutor();
    executorService.execute(restAPI);`

3) start the spring boot app

Expected behavior

app started successfully, Vaadin UI is shown and the REST API is available for the Client GUI via localhost:4567/microstream

Error

Exception in thread "Thread-4" java.lang.NoClassDefFoundError: org/eclipse/jetty/server/session/SessionHandler
at java.base/java.lang.ClassLoader.defineClass1(Native Method)
at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1027)
at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150)
at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:862)
at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:760)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:681)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:639)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526)
at spark.embeddedserver.jetty.EmbeddedJettyFactory.create(EmbeddedJettyFactory.java:51)
at spark.embeddedserver.EmbeddedServers.create(EmbeddedServers.java:80)
at spark.Service.lambda$init$2(Service.java:624)
at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: java.lang.ClassNotFoundException: org.eclipse.jetty.server.session.SessionHandler
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526)
... 13 more

Additional Context

This is the appropriate part of the pom. Please see the version mismatch for jetty components, maybe this is one of the causes of the NoClassDefFoundError: org/eclipse/jetty/server/session/SessionHandler.

 +- one.microstream:microstream-storage-restservice-sparkjava:jar:08.01.01-MS-GA:compile
 |  +- one.microstream:microstream-storage-restservice:jar:08.01.01-MS-GA:compile
 |  |  \- one.microstream:microstream-storage-restadapter:jar:08.01.01-MS-GA:compile
 |  +- com.sparkjava:spark-core:jar:2.9.3:compile
 |  +- org.eclipse.jetty:jetty-server:jar:12.0.5:compile
 |  |  +- org.eclipse.jetty:jetty-http:jar:12.0.5:compile
 |  |  |  \- org.eclipse.jetty:jetty-util:jar:12.0.5:compile
 |  |  \- org.eclipse.jetty:jetty-io:jar:12.0.5:compile
 |  +- org.eclipse.jetty:jetty-webapp:jar:9.4.46.v20220331:compile
 |  |  +- org.eclipse.jetty:jetty-xml:jar:12.0.5:compile
 |  |  \- org.eclipse.jetty:jetty-servlet:jar:9.4.46.v20220331:compile
 |  |     +- org.eclipse.jetty:jetty-security:jar:12.0.5:compile
 |  |     \- org.eclipse.jetty:jetty-util-ajax:jar:12.0.5:compile
 |  +- org.eclipse.jetty.websocket:websocket-server:jar:9.4.46.v20220331:compile
 |  |  +- org.eclipse.jetty.websocket:websocket-common:jar:9.4.46.v20220331:compile
 |  |  \- org.eclipse.jetty.websocket:websocket-client:jar:9.4.46.v20220331:compile
 |  |     \- org.eclipse.jetty:jetty-client:jar:12.0.5:compile
 |  |        \- org.eclipse.jetty:jetty-alpn-client:jar:12.0.5:compile
 |  +- org.eclipse.jetty.websocket:websocket-servlet:jar:9.4.46.v20220331:compile
 |  |  +- org.eclipse.jetty.websocket:websocket-api:jar:9.4.46.v20220331:compile
 |  |  \- javax.servlet:javax.servlet-api:jar:3.1.0:compile
 |  +- com.google.code.gson:gson:jar:2.10.1:compile
 |  \- javax.annotation:javax.annotation-api:jar:1.3.2:compile
hg-ms commented 7 months ago

Hello, The spark-java REST service was intended to provide the Api to standalone java apps too, that’s why it has jetty and other stuff included. There is currently no other implementation. As Spring Boot provides a great REST support you may implement the four required routes directly in your app to get rid of the sparkjava implementation. Please see https://github.com/microstream-one/microstream/discussions/549 for additional information.

to-do42 commented 7 months ago

Hi @hg-ms thank you for your fast response. In order to find my own solution later this year :-), I hope it's ok to attach it here. Maybe we find another source/place where we an include my simple but working solution for spring boot, e.g. for documentation purposes.

/**
 * disable security by
 *      AntPathRequestMatcher.antMatcher(HttpMethod.GET, "/microstream/**").permitAll();
 *
 * @see StorageRestServiceSparkJava.java
 */
@org.springframework.web.bind.annotation.RestController
@RequestMapping(value = "/microstream", produces = "application/json")
public class RestController {

    private final StorageRestAdapter storageRestAdapter;

    public RestController(StorageManager storageManager) {
        this.storageRestAdapter = StorageRestAdapter.New(storageManager);
    }

    @GetMapping("/root")
    public ViewerRootDescription root() {
        ViewerRootDescription result = storageRestAdapter.getUserRoot();
        return result;
    }

    @GetMapping("/dictionary")
    public String dictionary() {
        return storageRestAdapter.getTypeDictionary();
    }

    @GetMapping("/object/{objectId}")
    public ViewerObjectDescription object(@PathVariable("objectId") Long objectId,
            @RequestParam(value = "fixedOffset", defaultValue = "0", required = false) long fixedOffset,
            @RequestParam(value = "fixedLength", defaultValue = "" + Long.MAX_VALUE, required = false) long fixedLength,
            @RequestParam(value = "variableOffset", defaultValue = "0", required = false) long variableOffset,
            @RequestParam(value = "variableLength", defaultValue = "" + Long.MAX_VALUE, required = false) long variableLength,
            @RequestParam(value = "valueLength", required = false) Long valueLength,
            @RequestParam(value = "references", defaultValue = "false", required = false) boolean references) {

        // this can't be done within the method parameter declaration
        if (valueLength == null) {
            valueLength = this.storageRestAdapter.getDefaultValueLength();
        }
        ViewerObjectDescription result = storageRestAdapter.getObject(objectId, fixedOffset, fixedLength, variableOffset, variableLength, valueLength, references);
        return result;
    }

    @GetMapping("/maintenance/filesStatistics")
    public ViewerStorageFileStatistics statistics() {
        return storageRestAdapter.getStorageFilesStatistics();
    }

}
hg-ms commented 7 months ago

Thank you very much for publishing your solution here