Handling differences in the classpath order between running from Eclipse and running a deployed Gradle build


(dosview) #1

I have a Java application which I can run in Eclipse or build and deploy with Gradle. I’m dealing with a difference in runtime behavior between running from Eclipse and running from a deployed build, so I’m trying to make sure that the runtime classpath with all the dependencies on 3rd party JARs is the same in both cases (which most certainly is not).

In Eclipse, I can decide the priority of each JAR on the classpath by manually changing the order of the classpath entries. When the app starts from Eclipse, ‘System.getProperty(“java.class.path”)’ actually returns the entries in that order.

But in the deployed build, all those JARs just get copied into a ‘lib’ folder. When the app starts, ‘System.getProperty(“java.class.path”)’ just returns ‘.’ which I imagine means that ‘lib’ folder. I believe in this case the priority of each JAR on the classpath is given by the alphabetical order of the file names of the JARs, however my first question is whether I’m right about this.

Another question is how to make Gradle build in such a way that when I run the deployed build the runtime classpath will be the same as it is in Eclipse.


(Radim Kubacki) #2

The simplest way to find what classpath is used to run an application when started by Gradle build is to run this build with ‘–info’ and then you can compare this to your Eclipse set up.

I think that the classpath order should be honored when exporting Eclipse model from a Gradle. OTOH this is not sufficient to get the same behavior. First of all Eclipse has one classpath per project and it means that ‘main’ and ‘test’ source sets are mixed together with their dependencies. You can improve parts of this but it will always be just an approximation.

Why do you need to care about the order? In many cases it would be better to do a better dependency resolution to avoid conflicts. I know there are cases where the order matters but want to check if this is one of them.


(dosview) #3

Just to clarify, my app is not started by Gradle, it’s just built by it, then I start the app from its JAR with the ‘main’ method. I need to care about the order because if two JARs contain a class with the same qualified name and the same method, when the method is called the version of it that gets executed is the one of the class that comes first in the classpath. In my experience this is not too uncommon and often the differences in behavior between running a build and running from Eclipse are due to classpath differences. That my problem is due to that is just an hypothesis I want to verify. Thanks for your suggestions, I’m still a bit of a novice to Gradle so I’m processing them :slight_smile:


(Carlo Luib-Finetti) #4

If your app is a Eclipse PDE app, the classpath of Eclipse is quite different from regualar Java and is partly defined by MANIFEST.MF.


(dosview) #5

No, my app is not a plugin, it’s a regular Java web application, using Apache Tapestry.


(dosview) #6

Just for the record, I remote debugged the app running from a deployed build, and I verified that the problematic web page uses the class ‘org.apache.tapestry5.corelib.components.Label’, while when running from Eclipse it does not use that class (I’m trying to figure out what it uses instead). So classpath problems are still a likely possibility.


(dosview) #7

The problem was due to the fact that the Gradle script used to build was not putting certain files into the JAR. Those are Tapestry templates (.tml); without them Tapestry would resort to a default behavior, and when running from Eclipse those files were of course found instead.


(René Groeschke) #8

I agree with Radim that classpath ordering shouldn’t make a difference, but I see your point. The only chance I see here is to change the way you configure your “deployed app” classpath by explicitly list the jars you wanna include in your prefered order


(Radim Kubacki) #9

If you have these problems with classpath order then it is better to take a look at dependency resolving process - http://www.gradle.org/docs/current/userguide/dependency_management.html - and avoid having more versions of the same classeses. Likely if you fix this problem in your Gradle build it can get resolved in Eclipse when you sync your project. Of course there is a possibility that some Eclipse plugin will modify your project and inject something to your classpath that breaks the setup.

Re missing .tml files in a JAR: if you have a reproducible test case where this can be shown please let us know. If it is really a bug we can fix it. If not we can look for a proper solution.


(dosview) #10

I’m not sure why the classpath ordering shouldn’t make a difference, as long as there are classes with the same qualified name containing methods with the same signature and all the JARs end up deployed into the same “lib” folder. Also, considering how common it is for projects to use many 3rd party libs, I don’t see good reasons to assume that such kind of duplications don’t occur. However thanks for your suggestion, that might actually end up being the solution I go for.


(dosview) #11

You’re right, I think I should take a look at the dependency resolving process. Re missing .tml files in a JAR, I’m afraid I won’t be able to isolate the issue into a reproducible example, however I can confirm that while building by exporting an executable JAR from Eclipse produces a JAR with all the files present in that package (that is 2 ‘.class’ files and the 2 corresponding ‘.tml’ files), when I build with Gradle only the 2 ‘.class’ files end up in that package of the resulting JAR. But I don’t think it’s a Gradle bug, more likely I’m failing to code the Gradle script in such a way that also the non-Java files like the ‘.tml’ are included. I will have to figure out how to code the Gradle script to make it include all the files found in the project packages regardless of their file extension.


(dosview) #12

The problem is solved, as follows. The cause was that I have those ‘.tml’ files in a subpackage of ‘src/main/java/’ instead of ‘src/resources’, and the relevant part of the Gradle script was:

sourceSets {
 main {
  java {
   srcDir 'src/main/java'
  }
  resources {
   srcDirs = [ 'src/resources', 'src/main/webapp' ]
  }
 }
}

It does not mention any resource file under ‘src/main/java’, so Gradle wasn’t taking any from there. It was only taking Java files from there, just like it should. What worked was to add ‘src/main/java’ to the ‘resources’ bit and excluding the Java files:

sourceSets {
 main {
  java {
   srcDir 'src/main/java'
  }
  resources {
   srcDirs = [ 'src/resources', 'src/main/webapp', 'src/main/java/' ]
   exclude '**/*.class'
  }
 }
}

An alternative and maybe cleaner solution would have been to move the ‘.tml’ files into folders under ‘resources’, but we prefer to have each Tapestry template in the same folder as the corresponding Tapestry page, which of course is under ‘src/main/java/’.