I’ve often complained that students don’t get taught the important stuff they’ll need for programming in the real world when they study for computer science or even software engineering degrees. I was pretty sure I’d brushed up on my Java enough that I had all I needed to write Java code in the real world but I was proven very wrong over the weekend when I spent literally an entire day on what turned out to be one line of code. The problem was that I had a gaping hole in my understanding of how the JVM works and how programs can interact with it, I was totally ignorant of the power of Class Loaders.

It all started when I wanted to solve what I thought was a simple problem. I have been the lead developer on the EVE Portal (a collaborative writing environment to support research-based projects in schools) which is being developed by the EVE research group in NUI Maynooth. The portal is coming on nicely and we’re getting to the stage where we have to start tying up loose ends. This mainly involves fixing some of the ‘hacks’ we’d put in at the start of the rapid prototyping phase to get a working version out there quickly so we could start running trials with kids as early in the project as possible. None of these hacks were particularly bad, they mainly involved hard-coding in things that really shouldn’t be hard-coded in. Initially we were exceptionally strict about the deployment environment and basically insisted that out code be deployed to a certain directory on the server’s file system, and that it use a data-source with a particular name to connect to the Data Base via JNDI. This was perfectly acceptable while developing but made it impossible to deploy multiple instances of our portal on the same server. We hadn’t needed to do this till now since each developer was developing on their own machine and the latest ‘stable’ release was deployed on the main server. However, last Friday I was asked to set up a test environment for someone on the main server where out live instance lives. I thought it would be trivial, I’d just add a configuration file which would allow the location on the file system where data should be stored and the JNDI name for the DB to be specified. Simple, half an hour’s work and I’d be done. Well … not quite!

When you run multiple applications on the one tomcat server they all share the one JVM and that JVM has only one current path and that is /. So how do I get to a file in ‘my’ web-app when it could have any name and be in any folder? That’s what it took me an entire day and a lot of Googling and RTFMing to figure out. What made it all the more annoying is that I knew from the start it was possible because that’s how loggers like log4j and the Jakarta Commons logger work!

It all boils down to figuring out what is actually unique to each application deployed in a Java web container (like Tomcat) or EJB container (like JBoss). The answer is that each application has it’s own Class Loader and hence it’s own separate class-path despite all sharing a JVM with each other. In the case of a Tomcat web-app each app’s Class Loader sets the WEB-INF/classes/ folder within that webapp as the base for it’s class-path (and adds all jars in WEB-INF/lib too). But how does that help? Well a Class Loader is not just for loading classes, it’s also designed for loading the resources that classes need from within its class-path. So all I had to do was put my configuration file (eve-portal.xml) into WEB-INF/classes/ and load it with the line:

  1. URL myConfigFile = this.getClass().getClassLoader().getResource("/eve-portal.xml");

Well, to be entirely honest I ran into an extra complication because I needed to load this configuration file from within a static initializer rather than within and instance of a class. This meant my code for loading the configuration would be running within a static context and hence there is no this object available to get at my much needed Class Loader. This wasn’t a big problem at all though, I just threw together the following small helper class:

  1. class ClassLoaderHelper {
  2.   public void ClassLoaderHelper() {
  3.   }
  4.  
  5.   public ClassLoader getClassLoader() {
  6.     return this.getClass().getClassLoader();
  7.   }
  8. }

And then edited my one line of code from above to the following:

  1. URL myConfigFile = new ClassLoaderHelper().getClassLoader().getResource("/eve-portal.xml");

With those few lines of code I had my solution and in the process of writing them had filled in a gapping hole in my Java knowldege that I didn’t even realize I had! The thing is, loading resources into a Java program is something that you need to do a lot in the real world, yet in college we are not taught how to do it. We were only taught about the classes in java.io but these are utterly unsuited to this purpose because they involve hard-coding in file locations or assuming your program will always be run with the current path you expect, neither being appropriate in the real world. Simply put java.io is totally unsuited to the task of locating resources, it’s pretty much the worst tool for the job. There is an entire mechanism provided right within java.lang which is specifically designed for this purpose, Class Loaders. The Class Loader method makes only the assumption that each time your classes are run the resources they need will be located somewhere within your class-path. This is the most logical place to put them and these locations have to be accessible to the Class Loader because if they aren’t then your code can’t be loaded so it can’t run! BTW, the Class Loader can load resources from within JAR files or across the network as well as directly from the file system.

In four years of programming in Java for my BSc we were never even told the Class Loader mechanism existed. I’d mark that down as a pretty major over-sight! Why waste time teaching students to open resource files with java.io when what we really need to understand and utilize is the class loader mechanism?

Comments

11 Responses to “Java Class Loaders & Configuration Files -Why Wasn’t I Taught This in College?”

  1. Chris on December 1st, 2006 12:33 pm

    Instead of creating the ClassLoaderHelper, I think the following would work -

    URL myConfigFile = ThisClass.class.getClassLoader().getResource(“/eve-portal.xml”);

    For every class X, there is a “variable” X.class, which is the Class object for the class X.

  2. Bart B on December 1st, 2006 3:04 pm

    Hi Chris,

    The following quick and simple test shows you’re perfectly correct:

    public class ClassTest {
    static {
    System.out.println(ClassTest.class.getName());
    }
    public static void main(String args[]) {
    // do nothing
    }
    }

    Thanks very much, cutting out one helper class won’t speed up my app a lot but it will neaten the code and this is good to know for other reasons too!

    Bart.

  3. Jay on March 8th, 2008 10:11 pm

    But where do you store the configuration file if you have to deploy the application in WAR format?

  4. Bart B on March 9th, 2008 12:50 am

    Hi Jay,

    The configuration file goes into the WAR in the same place that your classes do. Basically, no matter how you package the application the configuration file has to be in the same place as the class files. When I make WARs it’s for webapps and in that case the config files go in WEB-INF/classes.

    Bart.

  5. Lori Gray on May 6th, 2008 9:10 pm

    I have my appConfig.properties files located with my source. I create a war file and install in on the IBM WebSphere Portal 6 server. Is there anyway for me to get the parameters from the properties file without having to have a servlet call? I would just like to reference the parameters in this file.

  6. Lori Gray on May 6th, 2008 9:23 pm

    And when I try to execute your code: I get URL cannot be resolved to a type… what am I missing here. Is there something out there that will allow you to reference a file that is contained within the WAR file without having to perform a request?

  7. Bart B on May 6th, 2008 11:25 pm

    Hi Lori,

    The file needs to be in your class path, so it needs to be with your class files, not with your source.

    Hope that helps,

    Bart.

  8. Mullen on July 30th, 2008 7:41 am

    hello Bart, I see a nice example, tx for that. but I have a little different task. I am using

    “InputStream vResourceAsStream = getClass().getClassLoader().getResourceAsStream(“001.config”);”
    …and so on, it works fine, the dir with the config file is defined in java build path dialog and when is war file built it goes to “WEB-INF/classes”, but what if i need the config file out of the war file? i am trying to make it done with ANT…and i struggle..tx for any idea :}

  9. Bart B on July 30th, 2008 2:49 pm

    Hi Mullen,

    The config file can go anywhere that’s within your classpath. If you need it somewhere outside your war file then that location needs to be in your classpath. Alternatively, if you want it to go in a pre-defined location on your file system then just use a regular FileInputStream to open the file.

    Hope that helps,

    Bart.

  10. mullen on August 5th, 2008 10:57 am

    yes, thank you a lot I finally put the real config file with the parameters outside and to the “WEB-INF/classes” dir I put just a file with the same name and with the path pointing to the real config file inside (something like: ../../../001.config) and it works fine..tx again :)

  11. pstanton on March 17th, 2009 10:41 am

    that’s one model, but what about when you’re deploying an app on multiple machines each requiring their own specific configurations, of which you as the programmer have no control/visibility.

    that’s when you’ll need a configuration file external to the deployment.

    under windows XP, the user’s ‘Application Data’ folder is the best candidate, on windows vista the users’s ‘AppData/Roaming’ is the best candidate. under unix/linux os’ i’d create a folder under whatever user.dir is.

Leave a Reply




Before you post a comment please remember that commenting on my blog is a privilege not a right. I won't approve comments that are obscene, offensive or insulting. For more info please read this post.

Subscribe without commenting