Class-Path Wildcards in Mustang

Most non-trivial Java programs depend upon external libraries in the form of jar files. Tools like Ant make it easier to cope with large collections of jar files at build time, but at runtime one often has to resort to passing a class path to the VM which explicitly names each and every jar file needed by the application. Such class paths are typically baked into a launch script or .BAT file, like so:

#! /bin/bash
LIB=$(dirname $0)/../lib
java -classpath $CP

This is fragile from a maintenance standpoint, of course, since you have to remember to update the script every time you add or remove a jar file.

A more flexible solution can be had at the cost of writing a more complex launcher that carefully figures out where all of the jar files for your program reside and constructs the required classpath. This can pretty quickly get out of hand, however, as witnessed by, e.g., the NetBeans 5.0 launch script for Unix or—worse—the equivalent C++ code for Windows.

Mustang addresses this annoying problem with the introduction of class-path wildcards. The basic idea is very simple: Instead of listing individual jar files in a directory, as in the example above, you can instead just use an asterisk to stand for all of the jar files that can be found in that directory:

java -classpath $LIB/'\*'

Note that you must quote the asterisk in order to prevent it from being interpreted by your shell and expanded into something that’s guaranteed not to work if there’s more than one jar file in the directory. When designing this feature we considered various other syntaxes that wouldn’t require quoting. In the end, however, we decided to go with the asterisk since it’s already familiar to Ant users and we figured that anyone clever enough to use class-path wildcards would also be clever enough to understand shell quotation.

So how exactly do class-path wildcards work?

Expansion of wildcards is done early, prior to the invocation of a program’s main method, rather than late, during the class-loading process itself. This simplifies both the semantics and the implementation and is also the most compatible approach. Each element of the input class path containing a wildcard is replaced by the (possibly empty) sequence of elements generated by enumerating the jar files in the named directory. If the directory foo contains a.jar, b.jar, and c.jar, e.g., then the class path foo/\* is expanded into foo/a.jar:foo/b.jar:foo/c.jar, and that string will be the value of the system property java.class.path.

The order in which the jar files in a directory are enumerated in the expanded class path is not specified and may vary from platform to platform and even from moment to moment on the same machine. A well-constructed application shouldn’t depend upon any particular order; if a specific order is required then the jar files must be enumerated explicitly in the class path.

Subdirectories are not searched recursively, i.e., foo/\* only looks for jar files in foo, not in foo/bar, foo/baz, etc. There’s no equivalent to Ant’s /\*\* construct, though that could be added later on.

A wildcard only matches jar files, not class files that happen to be in the same directory. If you want to load both class files and jar files from a single directory foo then you can say foo:foo/\*, or foo/\*:foo if you want the jar files to take precedence.

Class-path wildcards work in the -classpath (equiv. -cp) command-line option to the JVM launcher and most other command-line tools, and also in the CLASSPATH environment variable. They don’t work in in the Class-Path header of a jar-file manifest, nor do they work in the bootstrap class path (but you’d never use that anyway, right?).

Finally, class-path wildcards are an implementation-specific feature. They’re supported in Sun’s Mustang implementations, and they may be available in other implementations. As a feature of command-line tools, however, they’re not part of the Platform Specification, so there’s nothing that requires every implementation of Java SE 6 to support them.


This is great - I have been waiting for Java to embed this for years! Why is it being done in what appears to be a staged process though? i.e. "Recursive searches may come later". A simple "-r" option of the classpath could be used, so that it could be turned on and off. Also are there any plans to allow for pattern matching at a deeper level which could include classes? e.g. -classpath '\*.class':'pattern\*.jar' This way a pattern search could be done to include classes, jars or even zips that have specific names. The greatest example I can think of for having pattern matching for jars is with version numbers. If I create a jar (e.g. my_tools_1.0.1.jar) and then include it into a project "-classpath 'lib/my_tools\*.jar'" I can now add in new version of the "my_tools" jar (e.g. my_tools_1.0.2.jar) into my lib directory and still not include any other jars that I might have within the lib dir. What flexibility! It also helps with the upgrade path for projects. Kind Regards Philip Gordon

Posted by Philip Gordon on February 16, 2006 at 07:33 AM PST #

Yes, but giving into the temptation to throw everything into the CLASSPATH variable discourages proper organization, and ordering of jars does affect real-world applications where class names are often duplicated across jars. I'm not objecting to the feature, but its overuse could cause more harm than good in the long run.

Posted by Malcolm Sparks on February 17, 2006 at 11:28 AM PST #

Yes I do agree about the maintenance aspect - there are some who would/will abuse this feature. But should Java really be constrained in this way? There are many other features that could also be misconstrued within Java but it has been left up to the system architects and developers to ensure that the correct method was taken. Again, I do not disagree with your point. I just don't believe that Java should be too constrained so that projects are limited by what they can do.

Posted by Philip Gordonilip Gordon on February 19, 2006 at 09:04 AM PST #

Post a Comment:
Comments are closed for this entry.

This blog has moved to <script>var p = window.location.pathname.split('/'); var n = p[p.length - 1].replace(/_/g,'-'); if (n != "301") window.location = "" + n;</script>


« July 2016

No bookmarks in folder


No bookmarks in folder

RSS Atom