getResource() resource patterns
The Java class loader contains a convenience method getResource(), which is great for externalizing some static content from your code. In the past, I've used it almost exclusively with .properties files. Could there be a more ideal candidate for data that should be outside the code?
Recently, I wanted to leverage this concept to load text and html data. I needed to generate a email based on a static template. In order to support rich html email clients as well as plain text clients, it's necessary to send a MultiPart MIME email with both versions. My options were to hard-code the HTML/text templates in the Java code, or externalize them as files. I had a utility method lying around that would make this easy:
private static String txtTemplate, htmlTemplate; private static final String EMAIL_TEMPLATE_TXT = "LegacyAppointmentEmail.txt"; private static final String EMAIL_TEMPLATE_HTML = "LegacyAppointmentEmail.html"; static { try { txtTemplate = readFile(EMAIL_TEMPLATE_TXT); htmlTemplate = readFile(EMAIL_TEMPLATE_HTML); } catch (IOException e) { LOG.error(e); } } private static String readFile(String fileName) throws IOException { InputStream stream = LegacyAppointmentEmail.class.getResourceAsStream(fileName); BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); StringBuilder builder = new StringBuilder(); String line; while ((line = reader.readLine()) != null) builder.append(line).append("\n"); reader.close(); return builder.toString(); }
My first unit test failed on the line where the first file was loaded. Initially, I thought the path must be wrong. I played with various combinations, such as moving the file to the root, putting the full scope into the file path, etc. Eventually, I went back and looked at some working code I had written in the past. The only difference I could see was that is was a .properties file, not a .txt file. I renamed the file, and it worked!
I knew that wasn't right, but I put it aside and continued on. When I came back to it, I tried to come up with the smallest reproducible case I could. I thought that if I was still stumped, at least I could post it on stackoverflow.
public class Test { public static void main(String[] args) { assert Test.class.getResource("file.properties") != null; assert Test.class.getResource("file.txt") != null; } }
Note: file.properties and file.txt should both be blank files in the same directory as Test.java.
This test was still failing on the txt file, in IntelliJ. In order to get the cleanest repo possible, I saved Test.java to a new directory, and compiled and ran it from the command line using just the "javac" and "java" JDK command-line tools. Immediately, it started working.
Having isolated the issue to IntelliJ, I did a little digging. Even when I took all the classpath and "java" command arguments from IntelliJ and ran them manually, it still worked. Having eliminated runtime issues, I went into the IntelliJ compiler settings. The very first window shows the following option:
***Shakes fist at IntelliJ*** Oh well, it was a learning experience.