Java class loader bug: Caused by: java.io.IOException: Stream closed

14,827

According to this post, that exception could mean that the .yml file is simply not found. Since you changed your project structure, it is possible that the logic used to build the configPaths needs to be modified for the new structure. Did you try to log the content of configPaths to see if the paths are correct for the new structure?

Also make sure that the .yml files are included in the .war file. Some build systems handle resources differently than java class files.

Share:
14,827
Popcorn
Author by

Popcorn

Updated on June 28, 2022

Comments

  • Popcorn
    Popcorn almost 2 years

    I'm getting a strange bug regarding I believe class loader issues when I deploy my webapp to Tomcat. The bug doesn't appear when I run my webapp locally using Jetty. It seems like my input streams for my .yml resource files are being closed for some reason when they shouldn't be. This bug first appeared when I tried to convert my single module project into a multi module project. Before that, it was working fine on Tomcat using the exact same code:

    Caused by: org.yaml.snakeyaml.error.YAMLException: java.io.IOException: Stream closed
        at org.yaml.snakeyaml.reader.StreamReader.update(StreamReader.java:200)
        at org.yaml.snakeyaml.reader.StreamReader.<init>(StreamReader.java:60)
        at org.yaml.snakeyaml.Yaml.load(Yaml.java:412)
        at com.config.ConfigProvider.<init>(ConfigProvider.java:20)
        ... 49 more
    Caused by: java.io.IOException: Stream closed
        at java.io.PushbackInputStream.ensureOpen(PushbackInputStream.java:57)
        at java.io.PushbackInputStream.read(PushbackInputStream.java:149)
        at org.yaml.snakeyaml.reader.UnicodeReader.init(UnicodeReader.java:90)
        at org.yaml.snakeyaml.reader.UnicodeReader.read(UnicodeReader.java:122)
        at java.io.Reader.read(Reader.java:123)
        at org.yaml.snakeyaml.reader.StreamReader.update(StreamReader.java:184)
        ... 55 more
    

    Here's the line that causes the bug:

    String s = ConfigProvider.getConfig().getString("test");
    

    Here's the ConfigProvider class. It basically scans for all resource files of regex ^.*\\.config\\.yml$, converts it into a Map<String, Object>, and combines all the obtained Map<String, Object> into a single Map<String, Object>:

    1 public class ConfigProvider {
    2     protected static final String CONFIG_PACKAGE = ConfigProvider.class.getPackage().getName();
    3     protected static final Pattern CONFIG_PATH_REGEX = Pattern.compile("^.*\\.config\\.yml$");
    4 
    5     private static final ConfigProvider INSTANCE = new ConfigProvider();
    6     private Map<String, Object> configMap;
    7 
    8     protected ConfigProvider() {
    9         configMap = new HashMap<String, Object>();
    10 
    11        Set<String> configPaths = new Reflections(CONFIG_PACKAGE,
    12            new ResourcesScanner()).getResources(CONFIG_PATH_REGEX);
    13
    14        if (configPaths.isEmpty()) {
    15            throw new RuntimeException("no config paths found");
    16        }
    17
    18        for (String path : configPaths) {
    19            InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
    20            Map<String, Object> fullConfig = new Map<String, Object>((Map) new Yaml().load(inputStream));
    21        
    22            try {
    23                inputStream.close();
    24            } catch (IOException e) {
    25                throw new RuntimeException("error closing stream");
    26            }
    27
    28            MapUtils.merge(configMap, fullConfig);
    29        }
    30    }
    31
    32    public static ConfigMap getConfig() {
    33        return INSTANCE.configMap;
    34    }
    35 }
    

    Here's my project structure, titled Foo:

    - Foo (this is a module)
        - .idea
        - application (this is a module)
            - src
                - main
                    - java
                    - resources
                        - application.config.yml
                    - webapp
                - test
            - pom.xml
        - client (this is a module)
            - src
                - main
                    - java
                    - resources
                        - client.config.yml
                    - webapp
                - test
            - pom.xml
        - pom.xml
    

    ConfigProvider is a class I get from my parent pom file (Foo/pom.xml). I package a WAR file from the application module (Foo/application/target/application.war), and deploy it with Tomcat. Previously, my project was a single module project with just a Foo module being identical to application module. Then I added a client module and converted the project into a multi module project, and the problem has showed up. I think it's because my class loader is getting messed up due to the multiple modules. I've spent a lot of time trying to debug this and still haven't gotten anywhere. Anyone know what could be the cause, or can think of possible things to try?

    Please let me know if you need more info.

  • Popcorn
    Popcorn over 10 years
    Thank you, the problem was indeed trying to read a .yml file that didn't exist.
  • Minjeong Choi
    Minjeong Choi about 4 years
    thank you, same in my case, the error was because it wasn't recognizing my yml file. I'm using intellij, and I didn't set resource folder as Resources Root in my IDE. After setting the folder as Resource Root, it picked up my yml file and successfully read the file.
  • Bhesh Sejawal
    Bhesh Sejawal almost 4 years
    I have the file and still i am getting the same error, even though i have provided absolute path no lock