Repository: quarkusio/quarkus
Issue: QuarkusClassLoader.getResourceAsStream does not work with directories in quarkus:dev

**Describe the bug**
When your try to read a directory from the resources-folder using Thread.currentThread().getContextClassLoader().getResourceAsStream("test"), an IOException occurs. The directory can not be read as an InputStream, because the class Loaders uses Files.readAllBytes, which does not work with directories.

**Expected behavior**
According to the Java Spec, getResourceAsStream("test") should the same as getResource("test").openStream().

**Actual behavior**
When I use getResource("test").openStream() the InputStream is returned properly, but getResourceAsStream("test"). The follwing Exeception occurs
```
2020-08-28 11:55:57,017 ERROR [io.qua.ver.htt.run.QuarkusErrorHandler] (executor-thread-1) HTTP Request to /load/1 failed, error id: 4ca207b6-2f6f-4ad6-89c8-efe7959e90d3-3: org.jboss.resteasy.spi.UnhandledException: java.lang.RuntimeException: Unable to read /Users/christianradow/IntelliJ/classloader-test/target/classes/test
        at org.jboss.resteasy.core.ExceptionHandler.handleApplicationException(ExceptionHandler.java:106)
        at org.jboss.resteasy.core.ExceptionHandler.handleException(ExceptionHandler.java:372)
        at org.jboss.resteasy.core.SynchronousDispatcher.writeException(SynchronousDispatcher.java:216)
        at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:515)
        at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:259)
        at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:160)
        at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364)
        at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:163)
        at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:245)
        at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:73)
        at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:132)
        at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.access$000(VertxRequestHandler.java:37)
        at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler$1.run(VertxRequestHandler.java:94)
        at io.quarkus.runtime.CleanableExecutor$CleaningRunnable.run(CleanableExecutor.java:231)
        at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
        at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
        at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:2046)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1578)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1452)
        at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
        at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
        at java.base/java.lang.Thread.run(Thread.java:834)
        at org.jboss.threads.JBossThread.run(JBossThread.java:479)
Caused by: java.lang.RuntimeException: Unable to read /Users/christianradow/IntelliJ/classloader-test/target/classes/test
        at io.quarkus.bootstrap.classloading.DirectoryClassPathElement$1.getData(DirectoryClassPathElement.java:108)
        at io.quarkus.bootstrap.classloading.QuarkusClassLoader.getResourceAsStream(QuarkusClassLoader.java:337)
        at org.acme.quarkus.sample.ClassLoaderTestResource.testDirectory1(ClassLoaderTestResource.java:19)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:167)
        at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:130)
        at org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:638)
        at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:504)
        at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$2(ResourceMethodInvoker.java:454)
        at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364)
        at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:456)
        at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:417)
        at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:391)
        at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:68)
        at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:488)
        ... 20 more
Caused by: java.io.IOException: Is a directory
        at java.base/sun.nio.ch.FileDispatcherImpl.read0(Native Method)
        at java.base/sun.nio.ch.FileDispatcherImpl.read(FileDispatcherImpl.java:48)
        at java.base/sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:276)
        at java.base/sun.nio.ch.IOUtil.read(IOUtil.java:245)
        at java.base/sun.nio.ch.FileChannelImpl.read(FileChannelImpl.java:223)
        at java.base/sun.nio.ch.ChannelInputStream.read(ChannelInputStream.java:65)
        at java.base/sun.nio.ch.ChannelInputStream.read(ChannelInputStream.java:109)
        at java.base/sun.nio.ch.ChannelInputStream.read(ChannelInputStream.java:103)
        at java.base/java.nio.file.Files.read(Files.java:3158)
        at java.base/java.nio.file.Files.readAllBytes(Files.java:3212)
        at io.quarkus.bootstrap.classloading.DirectoryClassPathElement$1.getData(DirectoryClassPathElement.java:100)
        ... 37 more
```


**To Reproduce**
Steps to reproduce the behavior:
1. Create a directory named test in the resources folder
2. Use the following Resource:
```java
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.io.IOException;
import java.io.InputStream;

@Path("/load")
public class ClassLoaderTestResource {

    // Does not work using quarkus:dev
    @GET
    @Path("/1")
    @Produces(MediaType.TEXT_PLAIN)
    public String testDirectory1() {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        InputStream testResource = loader.getResourceAsStream("test");
        return testResource.toString();
    }

    // Works using quarkus:dev
    @GET
    @Path("/2")
    @Produces(MediaType.TEXT_PLAIN)
    public String testDirectory2() throws IOException {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        InputStream testResource = loader.getResource("test").openStream();
        return testResource.toString();
    }

}
```
3. Start the project using quarkus:dev
4. Test the /load/1 endpoint to reproduce the bug.

**Configuration**
```properties
# No additional config
```
**Environment (please complete the following information):**
 - tested on both Windows 10 and MacOS
 - Output of `java -version`: openjdk version "11.0.2" 2019-01-15
 - Quarkus version or git rev: tested on multiple Versions from 1.3.1.Final to 1.7.1.Final
 - Build tool (ie. output of `mvnw --version` or `gradlew --version`): Apache Maven 3.6.3

**Additional context**
The problem occurs because the directory is read using Files.readAllBytes(), which produces an IOException.


Changed files:
independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java
independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/classloader/ClassLoadingResourceUrlTestCase.java

diff --git a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java
index 439e4ab8bbf..61dae72b5b6 100644
--- a/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java
+++ b/independent-projects/bootstrap/core/src/main/java/io/quarkus/bootstrap/classloading/QuarkusClassLoader.java
@@ -323,6 +323,17 @@ public InputStream getResourceAsStream(String unsanitisedName) {
             for (ClassPathElement i : elements) {
                 ClassPathResource res = i.getResource(name);
                 if (res != null) {
+                    if (res.isDirectory()) {
+                        try {
+                            return res.getUrl().openStream();
+                        } catch (IOException e) {
+                            log.debug("Ignoring exception that occurred while opening a stream for resource " + unsanitisedName,
+                                    e);
+                            // behave like how java.lang.ClassLoader#getResourceAsStream() behaves
+                            // and don't propagate the exception
+                            continue;
+                        }
+                    }
                     return new ByteArrayInputStream(res.getData());
                 }
             }
diff --git a/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/classloader/ClassLoadingResourceUrlTestCase.java b/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/classloader/ClassLoadingResourceUrlTestCase.java
index d9d91d65aee..611d70bc06d 100644
--- a/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/classloader/ClassLoadingResourceUrlTestCase.java
+++ b/independent-projects/bootstrap/core/src/test/java/io/quarkus/bootstrap/classloader/ClassLoadingResourceUrlTestCase.java
@@ -4,6 +4,7 @@
 import io.quarkus.bootstrap.classloading.JarClassPathElement;
 import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
 import io.quarkus.bootstrap.util.IoUtils;
+import java.io.InputStream;
 import java.net.URL;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -51,6 +52,36 @@ public void testUrlReturnedFromClassLoaderDirectory() throws Exception {
         }
     }
 
+    /**
+     * Test that {@link QuarkusClassLoader#getResourceAsStream(String)} returns a stream for directory
+     * resources
+     *
+     * @throws Exception
+     * @see <a href="https://github.com/quarkusio/quarkus/issues/11707"/>
+     */
+    @Test
+    public void testResourceAsStreamForDirectory() throws Exception {
+        final JavaArchive jar = ShrinkWrap.create(JavaArchive.class)
+                .add(new StringAsset("a"), "a.txt")
+                .add(new StringAsset("b"), "b/b.txt");
+        final Path tmpDir = Files.createTempDirectory("test");
+        try {
+            jar.as(ExplodedExporter.class).exportExploded(tmpDir.toFile(), "tmpcltest");
+            final ClassLoader cl = QuarkusClassLoader.builder("test", getClass().getClassLoader(), false)
+                    .addElement(new DirectoryClassPathElement(tmpDir.resolve("tmpcltest")))
+                    .build();
+
+            try (final InputStream is = cl.getResourceAsStream("b/")) {
+                Assertions.assertNotNull(is, "InputStream is null for a directory resource");
+            }
+            try (final InputStream is = cl.getResourceAsStream("b")) {
+                Assertions.assertNotNull(is, "InputStream is null for a directory resource");
+            }
+        } finally {
+            IoUtils.recursiveDelete(tmpDir);
+        }
+    }
+
     /**
      * URLClassLoader will return URL's that end with a / if the call to getResource ends with a /
      */