akka / akka-http

The Streaming-first HTTP server/module of Akka
https://doc.akka.io/docs/akka-http
Other
1.34k stars 595 forks source link

Error when loading static content using AKKA-HTTP Java #2247

Open chineduekwunife opened 5 years ago

chineduekwunife commented 5 years ago

I'm trying out AKKA-Http in Java for the first time(akka-http_2.12 v 10.1.5, akka-stream_2.12 v 2.5.17), but can't seem to load static content for swagger UI. I've added the routes below and able to reach the json and yaml swagger endpoints via http://localhost:8080/api-docs/swagger.json and http://localhost:8080/api-docs/swagger.yaml However, the swagger UI endpoint i.e http://localhost:8080/swagger returns with 'There was an internal server error.' even though I have swagger-ui directory under src/main/resources folder. I've inspected the logs and don't quite understand what is happening..seems like a bug in getFromResource and getFromResourceDirectory functions. Similar issue was reported earlier

Route createRoute() {
        return route(
                path(PathMatchers.segment(generator.apiDocsPath()).slash("swagger.yaml"),
                () -> get(() -> complete(generator.generateSwaggerYaml()))),
                path(PathMatchers.segment(generator.apiDocsPath()).slash("swagger.json"),
                        () -> get(() -> complete(generator.generateSwaggerJson()))),
                path("swagger", () -> route (
                        getFromResource("swagger-ui/index.html"),
                        getFromResourceDirectory("swagger-ui")
                        )
                )
        );
    }

akka-spring-docker-system | 2018-10-06 08:27:11.292 ERROR 9 --- [lt-dispatcher-8] akka.actor.ActorSystemImpl               : Error during processing of request: 'java.lang.NullPointerException (No error message supplied)'. Completing with 500 Internal Server Error response. To change default exception handling behavior, provide a custom ExceptionHandler.
akka-spring-docker-system | 
akka-spring-docker-system | java.lang.NullPointerException: null
akka-spring-docker-system |     at akka.http.scaladsl.server.directives.FileAndResourceDirectives$ResourceFile$.apply(FileAndResourceDirectives.scala:286) ~[akka-http_2.12-10.1.5.jar!/:10.1.5]
akka-spring-docker-system |     at akka.http.scaladsl.server.directives.FileAndResourceDirectives.$anonfun$getFromResource$2(FileAndResourceDirectives.scala:106) ~[akka-http_2.12-10.1.5.jar!/:10.1.5]
akka-spring-docker-system |     at scala.Option.flatMap(Option.scala:171) ~[scala-library-2.12.4.jar!/:na]
akka-spring-docker-system |     at akka.http.scaladsl.server.directives.FileAndResourceDirectives.$anonfun$getFromResource$1(FileAndResourceDirectives.scala:106) ~[akka-http_2.12-10.1.5.jar!/:10.1.5]
johanandren commented 5 years ago

Trying to load a resource that isn't on the classpath returns null so it seems likely that is what is causing the NPE, if you look in your target directory, did the swagger-ui directory get copied somewhere there when you built your project? Also, when you run, is that somwhere added to the classpath (you can inspect your JVM process classpath with jvisualvm or jinfo)?

chineduekwunife commented 5 years ago

Trying to load a resource that isn't on the classpath returns null so it seems likely that is what is causing the NPE, if you look in your target directory, did the swagger-ui directory get copied somewhere there when you built your project? Also, when you run, is that somwhere added to the classpath (you can inspect your JVM process classpath with jvisualvm or jinfo)?

@johanandren

I've inspected the jar file and can find swagger-ui files in the classpath

c@c-VirtualBox:~/Projects/java/akka-spring-docker/code/akka-spring-docker-system/target$ jar -tf akka-spring-docker-system-1.0-SNAPSHOT.jar | grep swagger
BOOT-INF/classes/swagger-ui/
BOOT-INF/classes/swagger-ui/lib/
BOOT-INF/classes/swagger-ui/css/
BOOT-INF/classes/swagger-ui/images/
BOOT-INF/classes/swagger-ui/fonts/
BOOT-INF/classes/swagger-ui/lib/highlight.7.3.pack.js
BOOT-INF/classes/swagger-ui/lib/swagger-oauth.js
BOOT-INF/classes/swagger-ui/lib/underscore-min.map
BOOT-INF/classes/swagger-ui/index.html
BOOT-INF/classes/swagger-ui/images/pet_store_api.png
BOOT-INF/classes/swagger-ui/images/logo_small.png
BOOT-INF/classes/swagger-ui/images/favicon.ico
BOOT-INF/classes/swagger-ui/fonts/droid-sans-v6-latin-700.woff2
BOOT-INF/classes/swagger-ui/fonts/droid-sans-v6-latin-regular.svg
BOOT-INF/classes/swagger-ui/fonts/droid-sans-v6-latin-700.ttf
BOOT-INF/classes/swagger-ui/fonts/droid-sans-v6-latin-regular.eot
BOOT-INF/classes/swagger-ui/fonts/droid-sans-v6-latin-700.svg
BOOT-INF/classes/swagger-ui/fonts/droid-sans-v6-latin-700.woff
BOOT-INF/classes/swagger-ui/fonts/droid-sans-v6-latin-regular.ttf
BOOT-INF/classes/swagger-ui/fonts/droid-sans-v6-latin-regular.woff
BOOT-INF/classes/swagger-ui/fonts/droid-sans-v6-latin-regular.woff2
BOOT-INF/classes/swagger-ui/fonts/droid-sans-v6-latin-700.eot
BOOT-INF/classes/swagger-ui/lib/marked.js
BOOT-INF/classes/swagger-ui/lib/underscore-min.js
BOOT-INF/classes/swagger-ui/lib/backbone-min.js
BOOT-INF/classes/swagger-ui/lib/jquery.slideto.min.js
BOOT-INF/classes/swagger-ui/lib/jquery-1.8.0.min.js
BOOT-INF/classes/swagger-ui/lib/handlebars-2.0.0.js
BOOT-INF/classes/swagger-ui/lib/jquery.wiggle.min.js
BOOT-INF/classes/swagger-ui/lib/jquery.ba-bbq.min.js
BOOT-INF/classes/swagger-ui/css/reset.css
BOOT-INF/classes/swagger-ui/css/print.css
BOOT-INF/classes/swagger-ui/css/screen.css
BOOT-INF/classes/swagger-ui/css/typography.css
BOOT-INF/classes/swagger-ui/images/favicon-32x32.png
BOOT-INF/classes/swagger-ui/images/throbber.gif
BOOT-INF/classes/swagger-ui/images/wordnik_api.png
BOOT-INF/classes/swagger-ui/images/explorer_icons.png
BOOT-INF/classes/swagger-ui/images/favicon-16x16.png
BOOT-INF/classes/swagger-ui/o2c.html
BOOT-INF/classes/swagger-ui/swagger-ui.js
BOOT-INF/classes/swagger-ui/swagger-ui.min.js
BOOT-INF/lib/swagger-akka-http_2.12-0.14.0.jar
BOOT-INF/lib/swagger-core-1.5.18.jar
BOOT-INF/lib/swagger-annotations-1.5.18.jar
BOOT-INF/lib/swagger-models-1.5.18.jar
BOOT-INF/lib/swagger-jaxrs-1.5.18.jar
chineduekwunife commented 5 years ago

@johanandren

A closer look at the code in FileAndResourceDirectives.scala, seems that null is returned for directories, which might explain the NPE.

case "jar" ⇒
        val path = new URI(url.getPath).getPath // remove "file:" prefix and normalize whitespace
        val bangIndex = path.indexOf('!')
        val filePath = path.substring(0, bangIndex)
        val resourcePath = path.substring(bangIndex + 2)
        val jar = new java.util.zip.ZipFile(filePath)
        try {
          val entry = jar.getEntry(resourcePath)
          if (entry.isDirectory) None
          else Option(jar.getInputStream(entry)) map { is ⇒
            is.close()
            ResourceFile(url, entry.getSize, entry.getTime)
          }
        } finally jar.close()
johanandren commented 5 years ago

Doc on ZipFile.getEntry says null if not found. So I think the entry you expect is missing somehow. We should probably handle that and return a None if entry is null rather than fail though.

jrudolph commented 5 years ago

Doc on ZipFile.getEntry says null if not found. So I think the entry you expect is missing somehow. We should probably handle that and return a None if entry is null rather than fail though.

Probably, it's because the BOOT-INF part of the resource path is missing?

I guess, before we added the entry.isDirectory part, Option(jar.getInputStream(entry)) was actually guarding against missing files but now it fails even before and it seems we are missing a test case for the case when getFromResource points to a missing file.

Btw:

path("swagger", () -> route (
                        getFromResource("swagger-ui/index.html"),
                        getFromResourceDirectory("swagger-ui")
                        )

I wonder why that works at all. Shouldn't getFromResource("swagger-ui/index.html"), be guarded by pathEndOrSingleSlash or something else?

chineduekwunife commented 5 years ago

@johanandren @jrudolph

The exception is thrown at line 286 of FileAndResourceDirectives.scala if (entry.isDirectory) None Which suggests that the entry is present, but because it is a directory, None was returned which will cause a NPE.

Also, I added pathEndOrSingleSlash and got the same NPE result as before.

 Route createRoute() {
        return route(
                path(PathMatchers.segment(generator.apiDocsPath()).slash("swagger.yaml"),
                () -> get(() -> complete(generator.generateSwaggerYaml()))),
                path(PathMatchers.segment(generator.apiDocsPath()).slash("swagger.json"),
                        () -> get(() -> complete(generator.generateSwaggerJson()))),
                path("swagger", () -> route (
                        pathEndOrSingleSlash(() -> getFromResource("swagger-ui/index.html")),
                        pathEndOrSingleSlash(() -> getFromResourceDirectory("swagger-ui"))
                        )
                )
        );
    }
johanandren commented 5 years ago

It's not because None is returned, it is because entry is null, which causes a NPE when we try to call isDirectory on it.

chineduekwunife commented 5 years ago

OK then, so below comment from @jrudolph could be the fix for jar files

Probably, it's because the BOOT-INF part of the resource path is missing?

jrudolph commented 5 years ago

Probably, it's because the BOOT-INF part of the resource path is missing?

Actually, that statement was a bit sloppy. In fact, it depends how your classloaders are set up. It seems you are using some spring stuff, so it will depend on how your application is deployed exactly (we cannot really help with that).

Regarding your routes, this is still not correct:

                path("swagger", () -> route (
                        pathEndOrSingleSlash(() -> getFromResource("swagger-ui/index.html")),
                        pathEndOrSingleSlash(() -> getFromResourceDirectory("swagger-ui"))
                        )

It should be more like this:

pathPrefix("swagger", () -> route ( // everything that's inside `/swagger`
                        pathEndOrSingleSlash(() -> getFromResource("swagger-ui/index.html")), // `/swagger/` or `/swagger`
                        getFromResourceDirectory("swagger-ui") // everything else in `/swagger/...`
                        )

I marked this issue as a bug, because we need to improve the error message and not throw an NPE when a resource cannot be found.

jrudolph commented 5 years ago

The classloader / spring issue probably isn't a bug so it would make sense to continue those discussions on discuss.akka.io.

chineduekwunife commented 5 years ago

@jrudolph Most java applications use Spring, so I think it'll make sense to add support for Spring class loaders. For reference, below is the full content of the files in my jar file

c@c-VirtualBox:~/Projects/java/akka-spring-docker/code/akka-spring-docker-system/target$ jar -tf akka-spring-docker-system-1.0-SNAPSHOT.jar
META-INF/
META-INF/MANIFEST.MF
org/
org/springframework/
org/springframework/boot/
org/springframework/boot/loader/
org/springframework/boot/loader/data/
org/springframework/boot/loader/data/RandomAccessDataFile$1.class
org/springframework/boot/loader/data/RandomAccessDataFile$DataInputStream.class
org/springframework/boot/loader/jar/
org/springframework/boot/loader/jar/JarFileEntries$1.class
org/springframework/boot/loader/jar/JarFileEntries$EntryIterator.class
org/springframework/boot/loader/jar/JarFileEntries.class
org/springframework/boot/loader/jar/CentralDirectoryVisitor.class
org/springframework/boot/loader/jar/JarEntryFilter.class
org/springframework/boot/loader/jar/CentralDirectoryFileHeader.class
org/springframework/boot/loader/jar/CentralDirectoryEndRecord.class
org/springframework/boot/loader/jar/CentralDirectoryParser.class
org/springframework/boot/loader/jar/ZipInflaterInputStream.class
org/springframework/boot/loader/jar/Handler.class
org/springframework/boot/loader/archive/
org/springframework/boot/loader/archive/Archive$Entry.class
org/springframework/boot/loader/archive/Archive$EntryFilter.class
org/springframework/boot/loader/archive/Archive.class
org/springframework/boot/loader/archive/ExplodedArchive$FileEntryIterator$EntryComparator.class
org/springframework/boot/loader/archive/ExplodedArchive$FileEntryIterator.class
org/springframework/boot/loader/archive/ExplodedArchive$FileEntry.class
org/springframework/boot/loader/archive/ExplodedArchive$1.class
org/springframework/boot/loader/archive/ExplodedArchive.class
org/springframework/boot/loader/archive/JarFileArchive$EntryIterator.class
org/springframework/boot/loader/archive/JarFileArchive$JarFileEntry.class
org/springframework/boot/loader/archive/JarFileArchive.class
org/springframework/boot/loader/PropertiesLauncher$PrefixMatchingArchiveFilter.class
org/springframework/boot/loader/PropertiesLauncher$ArchiveEntryFilter.class
org/springframework/boot/loader/PropertiesLauncher$1.class
org/springframework/boot/loader/PropertiesLauncher.class
org/springframework/boot/loader/Launcher.class
org/springframework/boot/loader/MainMethodRunner.class
org/springframework/boot/loader/ExecutableArchiveLauncher.class
org/springframework/boot/loader/WarLauncher.class
org/springframework/boot/loader/JarLauncher.class
org/springframework/boot/loader/util/
org/springframework/boot/loader/util/SystemPropertyUtils.class
org/springframework/boot/loader/LaunchedURLClassLoader$UseFastConnectionExceptionsEnumeration.class
org/springframework/boot/loader/LaunchedURLClassLoader.class
org/springframework/boot/loader/data/RandomAccessData.class
org/springframework/boot/loader/jar/AsciiBytes.class
org/springframework/boot/loader/data/RandomAccessDataFile.class
org/springframework/boot/loader/data/RandomAccessDataFile$FileAccess.class
org/springframework/boot/loader/jar/JarURLConnection$1.class
org/springframework/boot/loader/jar/Bytes.class
org/springframework/boot/loader/jar/JarURLConnection$JarEntryName.class
org/springframework/boot/loader/jar/JarURLConnection.class
org/springframework/boot/loader/jar/JarFile$1.class
org/springframework/boot/loader/jar/JarFile$2.class
org/springframework/boot/loader/jar/JarFile$JarFileType.class
org/springframework/boot/loader/jar/JarFile.class
org/springframework/boot/loader/jar/JarEntry.class
org/springframework/boot/loader/jar/FileHeader.class
org/springframework/boot/loader/jar/StringSequence.class
BOOT-INF/
BOOT-INF/classes/
BOOT-INF/classes/com/
BOOT-INF/classes/com/nedu/
BOOT-INF/classes/com/nedu/akkahttp/
BOOT-INF/classes/com/nedu/akkahttp/config/
BOOT-INF/classes/com/nedu/akkahttp/routes/
BOOT-INF/classes/swagger-ui/
BOOT-INF/classes/swagger-ui/lib/
BOOT-INF/classes/swagger-ui/css/
BOOT-INF/classes/swagger-ui/images/
BOOT-INF/classes/swagger-ui/fonts/
META-INF/maven/
META-INF/maven/com.nedu.akka-http/
META-INF/maven/com.nedu.akka-http/akka-spring-docker-system/
BOOT-INF/classes/application.conf
BOOT-INF/classes/com/nedu/akkahttp/config/SpringActorProducer.class
BOOT-INF/classes/com/nedu/akkahttp/config/AkkaSpringConfig.class
BOOT-INF/classes/com/nedu/akkahttp/Application.class
BOOT-INF/classes/com/nedu/akkahttp/SwaggerRunner.class
BOOT-INF/classes/com/nedu/akkahttp/routes/UserRoutes.class
BOOT-INF/classes/com/nedu/akkahttp/routes/AppRoutes.class
BOOT-INF/classes/com/nedu/akkahttp/AppRunner.class
BOOT-INF/classes/com/nedu/akkahttp/SwaggerRunner$1.class
BOOT-INF/classes/application.properties
BOOT-INF/classes/swagger-ui/lib/highlight.7.3.pack.js
BOOT-INF/classes/swagger-ui/lib/swagger-oauth.js
BOOT-INF/classes/swagger-ui/lib/underscore-min.map
BOOT-INF/classes/swagger-ui/lib/marked.js
BOOT-INF/classes/swagger-ui/lib/underscore-min.js
BOOT-INF/classes/swagger-ui/lib/backbone-min.js
BOOT-INF/classes/swagger-ui/lib/jquery.slideto.min.js
BOOT-INF/classes/swagger-ui/lib/jquery-1.8.0.min.js
BOOT-INF/classes/swagger-ui/lib/handlebars-2.0.0.js
BOOT-INF/classes/swagger-ui/fonts/droid-sans-v6-latin-700.woff2
BOOT-INF/classes/swagger-ui/fonts/droid-sans-v6-latin-regular.svg
BOOT-INF/classes/swagger-ui/fonts/droid-sans-v6-latin-700.ttf
BOOT-INF/classes/swagger-ui/fonts/droid-sans-v6-latin-regular.eot
BOOT-INF/classes/swagger-ui/fonts/droid-sans-v6-latin-700.svg
BOOT-INF/classes/com/nedu/akkahttp/Supervisor.class
BOOT-INF/classes/swagger-ui/css/reset.css
BOOT-INF/classes/swagger-ui/css/print.css
BOOT-INF/classes/swagger-ui/css/typography.css
BOOT-INF/classes/swagger-ui/index.html
BOOT-INF/classes/swagger-ui/images/throbber.gif
BOOT-INF/classes/swagger-ui/images/favicon.ico
BOOT-INF/classes/swagger-ui/o2c.html
BOOT-INF/classes/swagger-ui/swagger-ui.min.js
BOOT-INF/classes/swagger-ui/fonts/droid-sans-v6-latin-700.woff
BOOT-INF/classes/swagger-ui/fonts/droid-sans-v6-latin-regular.ttf
BOOT-INF/classes/swagger-ui/fonts/droid-sans-v6-latin-regular.woff
BOOT-INF/classes/swagger-ui/fonts/droid-sans-v6-latin-regular.woff2
BOOT-INF/classes/swagger-ui/fonts/droid-sans-v6-latin-700.eot
BOOT-INF/classes/git.properties
META-INF/maven/com.nedu.akka-http/akka-spring-docker-system/pom.xml
META-INF/maven/com.nedu.akka-http/akka-spring-docker-system/pom.properties
BOOT-INF/classes/com/nedu/akkahttp/config/SpringExtension.class
BOOT-INF/classes/swagger-ui/lib/jquery.wiggle.min.js
BOOT-INF/classes/swagger-ui/lib/jquery.ba-bbq.min.js
BOOT-INF/classes/swagger-ui/css/screen.css
BOOT-INF/classes/swagger-ui/images/favicon-32x32.png
BOOT-INF/classes/swagger-ui/images/pet_store_api.png
BOOT-INF/classes/swagger-ui/images/logo_small.png
BOOT-INF/classes/swagger-ui/images/wordnik_api.png
BOOT-INF/classes/swagger-ui/images/explorer_icons.png
BOOT-INF/classes/swagger-ui/images/favicon-16x16.png
BOOT-INF/classes/swagger-ui/swagger-ui.js
BOOT-INF/lib/
BOOT-INF/lib/actor-common-1.0-SNAPSHOT.jar
BOOT-INF/lib/event-log-1.0-SNAPSHOT.jar
BOOT-INF/lib/user-registry-1.0-SNAPSHOT.jar
BOOT-INF/lib/swagger-akka-http_2.12-0.14.0.jar
BOOT-INF/lib/scala-library-2.12.4.jar
BOOT-INF/lib/scala-java8-compat_2.12-0.8.0.jar
BOOT-INF/lib/swagger-core-1.5.18.jar
BOOT-INF/lib/commons-lang3-3.7.jar
BOOT-INF/lib/jackson-annotations-2.9.0.jar
BOOT-INF/lib/validation-api-2.0.1.Final.jar
BOOT-INF/lib/swagger-annotations-1.5.18.jar
BOOT-INF/lib/swagger-models-1.5.18.jar
BOOT-INF/lib/slf4j-api-1.7.25.jar
BOOT-INF/lib/jackson-module-scala_2.12-2.9.6.jar
BOOT-INF/lib/scala-reflect-2.12.6.jar
BOOT-INF/lib/jackson-core-2.9.6.jar
BOOT-INF/lib/jackson-module-paranamer-2.9.6.jar
BOOT-INF/lib/paranamer-2.8.jar
BOOT-INF/lib/jackson-dataformat-yaml-2.9.6.jar
BOOT-INF/lib/swagger-jaxrs-1.5.18.jar
BOOT-INF/lib/jackson-databind-2.9.6.jar
BOOT-INF/lib/jsr311-api-1.1.1.jar
BOOT-INF/lib/reflections-0.9.11.jar
BOOT-INF/lib/javassist-3.21.0-GA.jar
BOOT-INF/lib/guava-20.0.jar
BOOT-INF/lib/javax.ws.rs-api-2.1.1.jar
BOOT-INF/lib/akka-http-cors_2.12-0.3.0.jar
BOOT-INF/lib/spring-boot-starter-2.0.5.RELEASE.jar
BOOT-INF/lib/spring-boot-2.0.5.RELEASE.jar
BOOT-INF/lib/spring-boot-autoconfigure-2.0.5.RELEASE.jar
BOOT-INF/lib/spring-boot-starter-logging-2.0.5.RELEASE.jar
BOOT-INF/lib/logback-classic-1.2.3.jar
BOOT-INF/lib/logback-core-1.2.3.jar
BOOT-INF/lib/log4j-to-slf4j-2.10.0.jar
BOOT-INF/lib/log4j-api-2.10.0.jar
BOOT-INF/lib/jul-to-slf4j-1.7.25.jar
BOOT-INF/lib/javax.annotation-api-1.3.2.jar
BOOT-INF/lib/spring-core-5.0.9.RELEASE.jar
BOOT-INF/lib/spring-jcl-5.0.9.RELEASE.jar
BOOT-INF/lib/snakeyaml-1.19.jar
BOOT-INF/lib/spring-boot-starter-jdbc-2.0.5.RELEASE.jar
BOOT-INF/lib/spring-jdbc-5.0.9.RELEASE.jar
BOOT-INF/lib/spring-tx-5.0.9.RELEASE.jar
BOOT-INF/lib/akka-http_2.12-10.1.5.jar
BOOT-INF/lib/akka-http-core_2.12-10.1.5.jar
BOOT-INF/lib/akka-parsing_2.12-10.1.5.jar
BOOT-INF/lib/akka-stream_2.12-2.5.17.jar
BOOT-INF/lib/akka-actor_2.12-2.5.17.jar
BOOT-INF/lib/config-1.3.3.jar
BOOT-INF/lib/akka-protobuf_2.12-2.5.17.jar
BOOT-INF/lib/reactive-streams-1.0.2.jar
BOOT-INF/lib/ssl-config-core_2.12-0.2.4.jar
BOOT-INF/lib/scala-parser-combinators_2.12-1.1.1.jar
BOOT-INF/lib/akka-http-jackson_2.12-10.1.5.jar
BOOT-INF/lib/akka-slf4j_2.12-2.5.17.jar
BOOT-INF/lib/lombok-1.18.2.jar
BOOT-INF/lib/jool-java-8-0.9.13.jar
BOOT-INF/lib/spring-context-5.0.9.RELEASE.jar
BOOT-INF/lib/spring-aop-5.0.9.RELEASE.jar
BOOT-INF/lib/spring-beans-5.0.9.RELEASE.jar
BOOT-INF/lib/spring-expression-5.0.9.RELEASE.jar
BOOT-INF/lib/jooq-3.10.8.jar
BOOT-INF/lib/jooq-meta-extensions-3.10.8.jar
BOOT-INF/lib/h2-1.4.197.jar
BOOT-INF/lib/hibernate-core-5.2.17.Final.jar
BOOT-INF/lib/jboss-logging-3.3.2.Final.jar
BOOT-INF/lib/hibernate-jpa-2.1-api-1.0.2.Final.jar
BOOT-INF/lib/antlr-2.7.7.jar
BOOT-INF/lib/jboss-transaction-api_1.2_spec-1.0.1.Final.jar
BOOT-INF/lib/jandex-2.0.3.Final.jar
BOOT-INF/lib/classmate-1.3.4.jar
BOOT-INF/lib/dom4j-1.6.1.jar
BOOT-INF/lib/hibernate-commons-annotations-5.0.1.Final.jar
BOOT-INF/lib/jooq-meta-3.10.8.jar
BOOT-INF/lib/jooq-codegen-maven-3.10.8.jar
BOOT-INF/lib/jooq-codegen-3.10.8.jar
BOOT-INF/lib/maven-plugin-api-2.2.1.jar
BOOT-INF/lib/maven-project-2.2.1.jar
BOOT-INF/lib/maven-settings-2.2.1.jar
BOOT-INF/lib/maven-profile-2.2.1.jar
BOOT-INF/lib/maven-model-2.2.1.jar
BOOT-INF/lib/maven-artifact-manager-2.2.1.jar
BOOT-INF/lib/maven-repository-metadata-2.2.1.jar
BOOT-INF/lib/wagon-provider-api-1.0-beta-6.jar
BOOT-INF/lib/backport-util-concurrent-3.1.jar
BOOT-INF/lib/maven-plugin-registry-2.2.1.jar
BOOT-INF/lib/plexus-interpolation-1.11.jar
BOOT-INF/lib/plexus-utils-1.5.15.jar
BOOT-INF/lib/maven-artifact-2.2.1.jar
BOOT-INF/lib/plexus-container-default-1.0-alpha-9-stable-1.jar
BOOT-INF/lib/classworlds-1.1-alpha-2.jar
BOOT-INF/lib/postgresql-42.2.5.jar
BOOT-INF/lib/HikariCP-3.2.0.jar
BOOT-INF/lib/junit-4.12.jar
BOOT-INF/lib/hamcrest-core-1.3.jar
jrudolph commented 5 years ago

It doesn't only depend on the contents of jar files but how the custom classloaders in your environment deal with those jar files. That's something we cannot debug here (but if you find out, it would make sense to add to the documentation).

chineduekwunife commented 5 years ago

@jrudolph I got it working by adding BOOT-INF to the path as follows

  return route(
                path(PathMatchers.segment(generator.apiDocsPath()).slash("swagger.yaml"),
                () -> get(() -> complete(generator.generateSwaggerYaml()))),
                path(PathMatchers.segment(generator.apiDocsPath()).slash("swagger.json"),
                        () -> get(() -> complete(generator.generateSwaggerJson()))),
                pathPrefix("swagger", () -> route ( // everything that's inside `/swagger`
                        pathEndOrSingleSlash(() -> getFromResource("BOOT-INF/classes/swagger-ui/index.html")), // `/swagger/` or `/swagger`
                        getFromResourceDirectory("BOOT-INF/classes/swagger-ui") // everything else in `/swagger/...`
                )
        ));
jrudolph commented 5 years ago

@chineduekwunife-kcom great!

baseman commented 5 years ago

Ran into the exact same problem. When trying to pull a resource using getFromResource when in a spring boot classloader is in the mix... null reference is throw when it cannot find the resourcePath.

I don't think it's been said... Spring Boot class loader is doing this to the resource url ... BOOT-INF/classes!/swagger/index.html ----notice the ! -----^

I know this is an implementation detail brought to you by Spring Boot & Company (see https://github.com/spring-projects/spring-boot/issues/7096), but can't akka-http team provide a simple directive for handling boot loader implementation rather than guys like us having to write hacks or custom directives for a spring boot class loader? The above direct path to BOOT-INF is kinda hacky and will only work when referring to resource directory from jar.

jrudolph commented 5 years ago

Maybe, even if it's completely unrelated to akka-http, let's document for the getFromResource* directives that your resources might have different paths than expected? For getFromResource we should create a better exception if a resource isn't found.

jrudolph commented 5 years ago

For getFromResource we should create a better exception if a resource isn't found.

I guess it already works correctly for simple class loaders. It seems that the spring class loader delivers an invalid URL for ClassLoader.getResource even if a resource cannot be found (when it should return null). Not sure if we can or should do something about this (other than adding documentation).