views:

820

answers:

4

I have a JAR file that contains an application as well as configuration files for that application. The application loads configuration files from the classpath (using ClassLoader.getResource()), and has its dependencies completely satisfied using the configuration files baked into the JAR file.

On occasion I want the application to be run with a slightly different configuration (specifically I want to override the JDBC URL to point to a different database) so I create a new configuration file, store it in the correct directory structure (which means in a directory /config of a classpath entry), and I want to do something like this:

java -cp new-config:. -jar application.jar

But I can't get the classpath to have the new-config path entry before the application JAR's contents. Is it hard-coded that the JAR's content is always the first thing on the classpath?

+1  A: 

It may not be possible using just the CLASSPATH. There are ways to get make the call to ClassLoader.getResource() use a static path to find the resource. If it is doing that, it is bypassing the CLASSPATH.

Kelly French
+1  A: 

The JAR archive specified by the -jar option, overrides all other values.

You would have to generally do it with an outside config file or build your own solution withod ClassLoader.getResource().

We use a custom solution to solve this - we load the internal properties like so:

final Properties p = new Properties();
p.load(DefaultConfiguration.class.getResourceAsStream("config.properties"));

We then load the external file in the same way and overwrite the internal values with the external ones.

For info on how class loading works see:

http://java.sun.com/javase/6/docs/technotes/tools/findingclasses.html

Gerd Klima
This is indeed the problem. I understand there is no way around that except launching the application in a different way - as suggested by alasdairg, or writing some custom loading code. Thanks.
Guss
+2  A: 

Why not just invoke the application without specifying -jar and instead name the application main class explicitly? This will allow you to put both your new-config and the application.jar on the classpath in the required order:

e.g. (assuming "new-config" is a directory containing the overridden properties file)

java -cp new-config:application.jar Application.Main.Class

I believe the name of main class can be found in the MANIFEST.MF file inside the jar ....

alasdairg
The main problem is that I don't actually use the -cp argument but specify the classpath in the manifest file, as the application requires many other external JARs - extracting all that will be rather error prone. But I bet I can write some script that extract the classpath from the manifest and builds a relevant command line automatically, so this is probably the answer I will use.
Guss
+2  A: 

When you use the -jar option to launch your application:

... the JAR file is the source of all user classes, and other user class path settings are ignored.

as described here. A workaround would be to specify the classpath in the jar file's manifest to include the additional path (described here).

However, given that you are only talking about amending configuration you may want to take a different approach that is not reliant on the classpath. For example, I typically configure my applications through Spring using property files to determine the location of databases, etc. My Spring configuration is consistent across test, QA and live environments but I pass a different property file as a command line argument when launching the app.

Spring Configuration Snippet

<bean id="MyDataSource" class="org.springframework.jdbc.datasource.SingleConnectionDataSource">
    <property name="url" value="jdbc:microsoft:sqlserver://${dbServer}:${dbPort};DatabaseName=${dbName}"/>
    <property name="username" value="${dbUserName}"/>
    <property name="password" value="${dbPassword}"/>
    <property name="suppressClose" value="false"/>
</bean>

Property File Snippet

dbServer=MyServer
dbPort=1433
dbName=MyDb
dbUserName=Me
dbPassword=foobar
Adamski
Passing the configuration file as an optional parameter is a good idea, but my application currently accepts quite a lot of parameters and adding another one will be problematic to the users. Thanks for the answer though.
Guss
How do you launch your application? I would have thought the parameters passed would be hidden to the user by launching the application via Webstart, .bat or .sh script, etc.
Adamski
No - the users launch the application themselves from the command line, passing in the required parameters - dates, files to process, etc.
Guss
I guess you could always make the users launch the app via a script or bat file that accepts JVM parameters or arguments and passes them to the JVM along with the property file? Difficult to tell if this is the best option without knowing more context about your users, etc.
Adamski
Eventually we went with the more complex implementation of changing the loader logic to look first in a couple of folders of the current working dir before falling back to the classpath loading semantics.
Guss