How to Verify Java Resource Paths with the Compiler

Code like Hello.class.getResourceAsStream("foo.bin") does not get evaluated until runtime. Worse, the method returns null if the resource is not found. This is not robust. Better to catch problems in the build process and get a meaningful error message.

Steps

  1. Check out the base project

    Follow the steps in How to Set Up, Compile and Run an AutoRes.uk Project

  2. Load some bytes using a stream

    Replace the contents of Hello.java with:

    package com.example;
    
    import uk.autores.handling.ResourceFiles;                    
    import java.io.IOException;
    import java.io.InputStream;
    
    @ResourceFiles(Hello.FOO)
    public class Hello {
      static final String FOO = "foo.bin";
    
      public static void main(String[] args) throws IOException {
        try (InputStream in 
              = Hello.class.getResourceAsStream(FOO)) {
          byte[] buffer = new byte[64];
          int r;
          while ((r = in.read(buffer)) >= 0) {
            System.out.write(buffer, 0, r);
          }
        }
      }
    }

    Compile and run the application:

    ./mvnw --quiet clean compile exec:java

    The contents of the file will be printed to the console.

  3. Cause an error condition

    Edit the constant to be a non-existant file:

      static final String FOO = "fooX.bin";

    Recompile and run the application.

    Maven emits an error about the file not existing.

  4. Make it simpler

    The FOO constant was added to avoid putting the string in two places. This ensures they are always the same but is not ideal. Replace the code with:

    package com.example;
    
    import uk.autores.InputStreams;          
    import java.io.IOException;
    import java.io.InputStream;
    
    @InputStreams("foo.bin")
    public class Hello {
      public static void main(String[] args) throws IOException {
        try (InputStream in = Example.foo()) {
          byte[] buffer = new byte[64];
          int r;
          while ((r = in.read(buffer)) >= 0) {
            System.out.write(buffer, 0, r);
          }
        }
      }
    }

    Compile and run the application.

    The package name is used to generate the class name. Try altering how the resource class is generated by setting some annotation properties:

    @InputStreams(
      value = {"foo.bin", "world.txt"},
      name = "ExampleResources",
      isPublic = true
    )