Problem with running application - classpath too big

I think in your case, the easiest thing for you to do is to drop the separate launcher jar, and add the Class-Path entry to your normal jar like this:

project('app:IntelliStat') {
    apply plugin:'application'
    version = 1.0
      dependencies {
        compile project(':api:itl')
        compile 'net.sf.jasperreports:jasperreports:4.5.1'
        compile 'org.springframework:spring:2.5.6.SEC01'
        runtime 'commons-cli:commons-cli:1.2'
        runtime 'commons-dbcp:commons-dbcp:1.3'
    }
   jar {
  manifest {
   attributes "Class-Path": configurations.runtime.files*.name.join(" ")
   }
 }
      startScripts {
        // clear up the classpath because the manifest has it.
        classpath = jar.outputs.files
    }
      task copyDatasource(type: Copy) {//copy our <datasource>.xml into intellistat-datasource.xml
            from('/datasource/')
        into('src/main/resources/')
        include(datasource + '.xml')
        rename(datasource + '.xml', 'intellistat-datasource.xml')
    }
      processResources.dependsOn ':app:IntelliStat:copyDatasource'
    mainClassName = "net.intellidata.intellistat.ReportRunner"
    jar.baseName = 'intellistat'
    distZip.baseName = 'intellistat'
}

I personally have all the “launcher” logic in a dedicated subproject without code or dependencies of it’s own, and subsequently i don’t suffer from these “timing” problems you are facing.

Why have a task ‘copyDatasource’ instead of configuring ‘processResources’? You shouldn’t copy stuff into a source directory (src/main/resources). The way that ‘launcherJar’ and ‘dependencies.runtime’ refer to each other seem circular. Not sure but this might be a problem.

Rolf: That got the classpath to show up in the manifest (though a little oddly, with line breaks in places that don’t make sense to me?)

Another problem now is that my transitive dependencies aren’t present in the lib dir (and itl.jar wouldn’t be right, it should be itl-1.0.jar)

Manifest-Version: 1.0
Class-Path: itl.jar jasperreports-4.5.1.jar spring-2.5.6.SEC01.jar com
 mons-cli-1.2.jar commons-dbcp-1.3.jar commons-collections-2.1.jar com
 mons-logging-1.1.1.jar commons-beanutils-1.8.3.jar commons-digester-2
 .1.jar bcmail-jdk14-138.jar bcprov-jdk14-138.jar bcprov-jdk14-1.38.ja
 r bcmail-jdk14-1.38.jar bctsp-jdk14-1.38.jar itext-2.1.7.jar jcommon-
 1.0.15.jar jfreechart-1.0.12.jar xml-apis-1.3.02.jar jdtcore-3.1.0.ja
 r castor-1.2.jar poi-3.7.jar stax-api-1.0.1.jar xmlbeans-2.3.0.jar ge
 ronimo-stax-api_1.0_spec-1.0.jar poi-ooxml-schemas-3.7.jar dom4j-1.6.
 1.jar poi-ooxml-3.7.jar jackson-core-lgpl-1.7.4.jar jackson-mapper-lg
 pl-1.7.4.jar commons-pool-1.5.4.jar

Peter:

The reason for the copyDatasource method is that we have multiple datasource files depending on which database we want our application to connect to. The specific datasource is defined as -Pdatasource=XXX at the command line. This is something we’re using in all of our database-dependent projects but if you have a better solution I’m eager to see it.

As to the circular dependency issue - do you have any suggestions? Every variation I’ve tried has fixed one part of this only to break something else

Rolf: I also tried adding a doLast {…} block around the manifest in the jar task, which gets the rest of the dependencies into the lib dir, but leaves me with an empty manifest again. Same with jar << {…}

Still, I don’t see a reason to use a separate task. You should just be able to configure ‘processResources’ with from/include/rename.

Peter: Can you give me an example that would pull the datasource as copyDatasource does while still getting the other resources from the src/main/resources dir?

Try this:

processResources {
  from('/datasource') {
    include(datasource + '.xml')
    rename(datasource + '.xml', 'intellistat-datasource.xml')
  }
}

Thanks Peter, that seems to work, and will make my build scripts much nicer. Do you have any suggestions on the main issue I’m trying to resolve in this thread?

Any more ideas? The only other option I’ve found so far is to make a so-called “fat” jar and include the all the dependencies physically inside the jar, but that seems like a truly monumental hack… (not that I have that working, either)

Ok, that path failed as well. I’m really stuck on this one

The problem seems to be your hierarchical layout (itl being a subproject of api, etc).

I have made a small example based on your build file but with a flat project layout, and this seems to include all jars in your lib folder, and populate the manifest’s Class-Path properly: http://wikisend.com/download/853588/test.zip

Note: the zip file will be gone in a week

This does not mean you have to refactor your entire project’s folder structure: your projects can still live in subfolders, but then you need some logic in your settings.gradle file setting the apropriate folder for each project. Many projects do this, and in fact, gradle itself does this as well: https://github.com/gradle/gradle/blob/master/settings.gradle

I’m not sure if the problems you’ve encountered could be interpreted as a bug with hierarchical project layouts, since flat layouts seem to work just fine, but maybe one of the gradle devs can at least shed some light as to why a flat project layout seems to resolve dependencies a little bit different then hierarchical layouts:)

Cheers Rolf

Rolf - Thanks, this looks promising. But I’m afraid i’m not terribly familiar with groovy so I’m not entirely sure what each of the statements in that settings.gradle means. (Not a scripting language guy, I’m rather fond of Java and its explicit variable definitions and strong typing).

Anyway, do you know of a place where I can go to get a better understanding of what is being done in that file so I can adapt it? Or perhaps you can give me a brief example?

If it helps, this is the contents of our settings.gradle file:

include 'api:core', 'api:intellispec', 'api:itl', 'api:pnf', 'api:pricing', 'api:schema', 'api:sleuth', 'api:util'
include 'app:DataC', 'app:DataExpress', 'app:DPU2', 'app:IntelliData', 'app:IntelliStat', 'app:IQ', 'app:IQE', 'app:ISManager', 'app:ITLDemo', 'app:PNFManager', 'app:PNGenerator'

As you can see (or perhaps guess) we have api-level projects that support our user-facing app level projects.

if you are not able or allowed to change the physical structure of the folders (i.e. move all folders under api/ and app/ up one level), then you need to do something like this:

include 'core-api', 'intellispec-api', 'itl-api', 'pnf-api', 'pricing-api', 'schema-api', 'sleuth-api', 'util-pi'
include 'DataC-app', 'DataExpress-app', 'DPU2-app', 'IntelliData-app', 'IntelliStat-app', 'IQ-app', 'IQE-app', 'ISManager-app', 'ITLDemo-app', 'PNFManager-app', 'PNGenerator-app'
  rootProject.children.each {project ->
 int dash = project.name.indexOf('-')
   // grab 'api' or 'app' suffix
 String suffix = project.name.substring(dash + 1)
 String folder = project.name.substring(0, dash)
   // inform gradle the project is located in suffix/folder, relative
 // to the directory of this settings.gradle
 project.projectDir = new File(new File(settingsDir, suffix), folder)
   // debug - remove later ;)
 println "${project.name} is located at ${project.projectDir}"
}

Note that this changes the name of each of your projects, so you need to update all project dependencies to use this new name.

Rolf - I implemented the change you recommended, but with no change in net behavior. As of right now I have a populated class-path in my manifest, but the lib dir is still missing a lot of my dependencies.

You said this earlier: “I personally have all the “launcher” logic in a dedicated subproject without code or dependencies of it’s own, and subsequently i don’t suffer from these “timing” problems you are facing.”

How does that look in your build script?

Also - what version of gradle are you using? maybe there’s a difference in behavior between whatever you’re using and milestone 9?

I tried a dedicated subproject in the minimal example i uploaded, and due to the project hierarchy it did not work either. Once i rearranged it to a flat layout, i got all dependencies in.

Here is how my root build.gradle file looks like: https://gist.github.com/1b78ac56ea5dd6e7fff8

I had this buildscript since milestone-3, and currently i am on rc-2. Which reminds me, i should the wrapper.

Peter - you’re a core developer. Can you tell me if this issue is a gradle bug in hierarchical projects or some kind of intended behavior?

Can someone throw some light around this? I have the same issue going on … but could not get this working… we dont use startscripts or application… for our pacaking… is it because we have the class path manifest just mentions the name of the jar but not the actual class path ?? cause I do see manifest file inside the launcherJar… but those are not the correct paths from where gradle should pull all those?

Rolf Suurd - Can you actually repost that project where you made it work ?

Hi bak2k2,

Sorry, i can’t. The project where i have used this solution is owned by my former company, and is not open source.