Thursday, December 28, 2017

Command line classpath is ignored when using -jar

I was a little shocked today by my discovery of -classpath (-cp) parameter being ignored silently by Java Virtual Machine, when -jar is provided. Look at this (my jar file prints out its working directory and classpath):
java -cp /home:./ -jar ./build/jarclassscanning.jar

Working Directory = /home/me/projects/jarclassscanning
----------------
Classpath elements:
/home/me/projects/jarclassscanning/build/jarclassscanning.jar
----------------

However, if I run the same jar without -jar parameter, here's what it prints out (I've added a couple of directories to -cp just to show that it works):
java -cp /home:./:./build/jarclassscanning.jar my.package.jarclassscanning.BaseClass

Working Directory = /home/me/projects/jarclassscanning
----------------
Classpath elements:
/home/
/home/me/projects/jarclassscanning/
/home/me/projects/jarclassscanning/build/jarclassscanning.jar
----------------

So if your jar uses classes from other jars (or just plain old .class files) and you have to specify classpath, one solution is to add classpath elements to the manifest file - https://docs.oracle.com/javase/tutorial/deployment/jar/downman.html. Keep in mind, however, that you've got to use spaces to separate them, not colons.

Also I've ran into another thing - when specifying jar files as classpath elements using Class-Path field of the manifest file using relative paths, they had to be relative to the location of my jar (the one that I give to JVM using -jar).

So, since my folder structure looked like this:
build
    myjar.jar
jdbc
    postgresql-42.1.4.jar
amqp
    amqp-client-4.2.0.jar
sl4j
    slf4j-simple-1.7.25.jar
    slf4j-api-1.7.25.jar

The Class-Path field of the manifest in myjar.jar ended up looking like this (note the ".."'s):
Class-Path: ../jdbc/postgresql-42.1.4.jar ../amqp/amqp-client-4.2.0.jar ../slf4j/slf4j-simple-1.7.25.jar ../slf4j/slf4j-api-1.7.25.jar

Tuesday, December 26, 2017

Apache Ant modifies classpath

Just a quick note - I have discovered this thing while digging into Java class loading / reflection. When running my test program from command line, like so:
java -ea -cp ./build/jarclassscanning.jar my.package.jarclassscanning.BaseClass
(yes, I specify my main class directly and just add my jar file to the classpath)

my classpath contains what I expect it to contain:
/home/me/projects/jarclassscanning/build/jarclassscanning.jar

But if I use my simplistic ant build.xml, which contains target run:
<target name="run">
  <java classname="my.package.jarclassscanning.BaseClass">
    <arg value="-ea"/>
    <classpath>
      <pathelement location="./build/jarclassscanning.jar"/>
    </classpath>
  </java>
</target>

this is what my classpath becomes (printed out from the test program itself):
/home/me/apps/apache-ant-1.9.7/lib/ant-launcher.jar

However, if I add fork="true" to the java task (just as you should if you use jar attribute, which I don't):
<target name="run">
  <java classname="my.package.jarclassscanning.BaseClass" fork="true">
    <arg value="-ea"/>
    <classpath>
      <pathelement location="./build/jarclassscanning.jar"/>
    </classpath>
  </java>
</target>

everything's back to normal:
/home/me/projects/jarclassscanning/build/jarclassscanning.jar