Search code examples
javaclasspathclassloaderplayframework-2.3properties-file

How to load external file into classpath Play Framework 2.3


I have a need to load an external file into the classpath using Play Framework 2.3 (Java)

Stipulations:

  • The external file cannot live inside my Play app (i.e. the /conf and /lib directories, etc. are not an option)

  • The file needs to be a .properties or a .conf file so I can specify property values

Here's my scenario:

  • I have a custom JAR that has some code which is looking for a specific file (let's call it myproperties.properties) in the classpath when being used. The way that I'm attempting to find myproperties.properties is by doing this inside a class that resides inside that custom JAR:

    ClassLoader classLoader = com.my.package.MyCustomJavaClass.class.getClassLoader();
    InputStream inputStream = classLoader.getResourceAsStream("/path/to/myproperties.properties");
    

    I have access to change the properties file name and the path to it inside the JAR.

  • My Play Framework App (using Java) has this custom JAR in it's /lib folder, so it gets automatically added to the classpath (this is tested and works correctly). My Play App calls MyCustomJavaClass when it first loads the / route (index route), so the class loader and input stream code above gets kicked off when I hit my play app in the browser.

Problem:

  • I have not been successful in my attempts to load /path/to/myproperties.properties into the classpath when starting the Play App in a way that my code in the custom JAR can see it.

    I've been attempting to start play with the classpath command like so in an attempt to feed the JVM the external file:

    activator start -J-classpath "-J-classpath:/path/to/myproperties.properties"
    

    I'm adding -J-classpath; to the beginning of the path in an attempt to copy everything that's currently in the classpath and then just adding my single, external file. However, doing this doesn't seem to be working (i.e. my inputStream is null).

Questions:

  • Am I doing the activator start -J-classpath command correctly when starting the play app? Other variations in an attempt to copy the existing classpath first were not allowing the play app to start, but this command at least starts my app.

    Reference (Specifying additional JVM arguments): https://www.playframework.com/documentation/2.3.x/ProductionConfiguration

  • What are some other ways that I could possibly get this done? I've explored overriding the application.conf file using activator start -Dconfig.file=/path/to/application-override.conf and putting my properties inside the new application-override.conf file. However, it doesn't seem to put that file into the classpath for MyCustomJavaClass to find using the Class Loader. Maybe I'm doing this command incorrectly as well?

  • Is it possible that somehow the Play Framework classpath is separate from the classpath that my custom JAR is seeing? I've been under the assumption that it's all in one JVM and classpath.


Solution

  • here's the solution I came up with, hopefully it helps someone else out there:

    • in my "upper environments" (AWS servers) where my play app is deployed, I put an application-override.conf file in the conf folder in the play framework app directory

    • the application-override.conf is the exact same as my application.conf but I have some custom properties in both whose values are different in each environment that the play app lives on

    • my play framework app is in a git repo, which is cloned on each upper environments, so I added application-override.conf to the .gitignore (I don't want it checked it to the repo so it only lives on the servers)

    • when starting the play app, I now use activator start "-Dconfig.trace=loads -Dconfig.file=conf/application-override.conf". this will override the application.conf file with application-override.conf and application-override.conf will be in the JVM classpath that play uses to run the app (since it's in the conf directory). -Dconfig.trace=loads spits out more logging to let you know if the .conf file was loaded properly or not; it's not a necessary flag if everything is working properly.

    • on the java side, in my custom JAR, I can now do the following:

      Properties properties;
      InputStream stream;
      ClassLoader classLoader = com.my.package.MyCustomJavaClass.class.getClassLoader();
      
      // first, look for application-override.conf in the classpath (upper environments)
      stream = classLoader.getResourceAsStream("application-override.conf");
      
      // if null, check for application.conf (local environment)
      if (stream == null) {
          stream = classLoader.getResourceAsStream("application.conf");
      }
      
      properties = new Properties();
      properties.load(stream);
      stream.close();
      

    other notes:

    • I thought about doing a symlink/softlink in the conf directory and put the application-override.conf file somewhere else on my environment, but prior to Play 2.4, you can't have symlinks in the conf directory, so I just put the actual application-override.conf file in the conf folder

    • The application-override.conf file has different property values for each "upper environment", otherwise I would have just delivered a single override file to the git repo. And in the custom JAR, I didn't want to put in logic that looked for varying file names like dev-override.conf, pre-prod-override.conf, prod-override.conf, etc. I wanted a single upper environments override file.

    • I didn't have success with the -classpath=/path/to/myproperties.properties or -J-classpath=/path/to/myproperties.properties commands in conjuction with activator start. nor did I have success with attempting to append to the classpath, e.g. activator start -J-classpath=-J-classpath:/path/to/myproperties.properties or other similar combinations

    • going the route of putting properties in an application-override.conf file actually killed two birds with one stone for me because I've been wanting to make some environment specific changes by having overriding .conf files on each of my environments as well as custom properties

    • the HOCON format of the .conf files required me to put double quotes around my property values due to the nature of my property values. when reading in those properties in Java, the quotes were still there for me, so I had to do an str.replace("\"","") when reading in those properties