• Share this article:

Many classpaths to enlightenment

Wednesday, July 25, 2007 - 21:44 by Wayne Beaton

Something that a lot of people don’t understand about Eclipse Java development tools (JDT) is that the “build” path and the “runtime” classpath are actually two different things. The “build” path, oddly enough, is used by the Java compiler when building a project. If you’re building a plain-old Java project, then this is pretty much the only path that you need to care about, since—in the absence of an explicitly-provided classpath in a run configuration—Eclipse will use the build path at runtime. This works out great when you’re developing a Java application and want to test it: it just works.

However, when you attempt to deploy that application outside of the warm and comfortable environment that is Eclipse, you have to set up the runtime classpath yourself. There are few ways to do this; you can set up an environment variable, just add a bunch of JAR files to the command line that starts your Java application, or make a shell script that configures everything just so. In short, the runtime classpath is different from the build path and the separation of the two of them can be pretty painful (especially when you leave the aforementioned warm and comfortable environment that is Eclipse).

When you build a plug-in/bundle for Eclipse, the world is a little different. The “build” path is still used for building the Java code and is absolutely necessary. However the build path is completely ignored by the Equinox/OSGi runtime environment. In the runtime environment, the bundle manifest (manifest.mf) is king and your plug-in/bundle’s runtime classpath is defined by the entries contained in that file. The Plug-in Development Environment (PDE) does a wonderful job of letting you define your dependencies in one place: it automatically configures the build path for you (using a wonderful dynamic library) based on the dependencies you specify in the bundle manifest. When you’re building plug-ins, you never have to look at the build path. This can make things a little (or a lot) confusing. If you add something to the build path of a plug-in project, the JDT will happily compile the code and will not report any errors (at least not related to the library you’ve added). However, at runtime, the build path is ignored and you’re bound to get a bunch of ClassNotFoundExceptions. Bummer.

Web projects have a similar issue. A web project needs various libraries in order to compile. These required libraries are need to be on the build path so that the JDT can find them. This includes libraries (like j2ee.jar for example) that are provided by the application server. I need these libraries at build time so that JDT can actually compile my code, but I don’t need to provide them separately at runtime since the application server will make them available.

Specifying dependencies between projects that are to be deployed on an application server is subject to the classloading strategy provided by the server. In Java EE, there’s no notion of projects; there is instead a notion of modules. Eclipse represents each Java EE module as a project. That is, a web module, which are represented as a WAR file on the application server, is represented by a Dynamic Web Project in the Eclipse workspace. So-called “utility” libraries, represented as JAR files on the application server, are represented as Java projects in Eclipse. I vaguely recall that there this thing called Enterprise JavaBeans, but we’ll save those for a later discussion. The final piece is the Enterprise Application. An Enterprise Application contains these modules, so that in the ideal world, an entire Java EE application containing multiple modules is contained in a single EAR file (You think that your hygiene is bad… I have a WAR in my EAR!).

Visibility between modules is accomplished through the manifests in a manner very similar to plug-ins. A module’s manifest declares all the other modules within the EAR that can be seen, and the runtime environment makes sure that everybody can see what they need to see. If this visibility between modules is not explicitly specified, then you’re going to get a bunch of ClassNotFoundExceptions at runtime (even if you’ve got your build path all configured so that everything compiles). Again, just like with plug-in projects, the Java build path is ignored at runtime. The Java build path is something that’s required a build time.

Update! I had previously mistyped the name of the Web Tools Platform.

Fortunately, the Web Tools Platform (WTP) takes care of this for you. As you are building your Java EE modules, you must specify your runtime dependencies between modules using the “J2EE Modules” tab on the “J2EE Module Dependencies” properties page (you can also add utility JARs to a web project’s lib directory using the “Web Libraries” tab). Just like the PDE, WTP automatically configures the build path so that everything you need to see at runtime is also available at build time If you’re building a web application, you should never have to look at the Java Build Path for your project (WTP also puts the libraries contributed by your application server onto the build path for you).

This extends to Java projects as well. If you have dependencies between multiple “utility” JARs, you need to manage those dependencies using the “J2EE Module Dependencies” property page to make sure that the right visibility is available at runtime. If your utility JAR needs to access libraries provided by the application server, you can set the “Targeted Runtimes” to include your server and, again, the build path will be automatically configured for you. For utility projects, this may seem a little weird, but if you’re accessing code in other modules and in application server libraries, you need this information for your application to work at runtime.

The short version of all this, is that you really only ever need to mess around with the Java Build path if you’re building Java applications.