I am building a library, which has separate entry points (like server, gui), with different dependencies. Each entry point is in a separate sub project with its own dependencies.
In the root project, where I start the build from, I want to select the entry point, and only build with that dependencies. That is working.
But I want to instantiate a class (eg MainClass) of the root project from the library entry point and I cant add the root projects class path to the dependency. (Diagram)
Root projects build file looks like this now:
dependencies{
implementation project(':server')
}
It seems to me that it would be easier to understand and clearer if the entry point projects depended on the core API instead of vice versa.
You could have project structure like:
settings.gradle
core/
build.gradle
src/
server/
build.gradle
src/
gui
build.gradle
src/
server and gui project build.gradle files should contain:
dependencies {
implementation project(':core')
}
The project that uses the library could depend on Server and/or GUI projects and instantiate the necessary class (ServerEntry or GuiEntry) directly.
If you want to be able to switch between different entry point implementations without changing the code in the project that uses the entry point instance I'd suggest using a dependency injection framework (Spring, Guice, Dagger). Dependency injection would help to separate configuration (binding interfaces to classes) from the actual application.
I solved my problem with Composite build. I added includeBuild '../path-to-lib' in the settings.gradle, Than I created a subproject Project to the library with the proper package and class name. On run it throws an error, that the developer should create this class.
Its also important to add all the subprojects to the same group:
allprojects{
group = 'library-group'
}
In the host project, I can depend on the library:
dependencies{
implementation module('library-group:suproject')
}
Now Gradle automatically overwrites the Classes on the same route as the documentation suggest, and I can finally inject my host project into the lib, and compile it as a whole.
Related
I have a gradle project P which has module A and B. Module A has this jackson dependencies:
...
dependencies {
...
compile 'com.fasterxml.jackson.core:jackson-core:2.12.0-rc1'
compile 'com.fasterxml.jackson.core:jackson-annotations:2.12.0-rc1'
compile 'com.fasterxml.jackson.core:jackson-databind:2.12.0-rc1'
compile 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.12.0-rc1'
...
}
...
and module B uses module A, and has no need for including this dependencies because jackson usage is encapsulated in module A. But when code executed from module B reaches a statement that invokes code from Module A using it, I get exception:
java.lang.NoClassDefFoundError: com/fasterxml/jackson/dataformat/xml/XmlMapper
If I add the same dependencies to Module's B gradle.build file, the code works.
The question is, why would I include them if Module A does not use the library?
Shouldn't dependencies in Module A be compiled, packaged, so that when Module A is used elsewhere, its code works (using its included dependencies such as jackson library as in this example)
Disclaimer: I don't know Gradle, but this sounds like a problem common to Maven and Gradle.
The fact that you can build the module, but not run the module, means that somewhere you are not bringing the transitive dependencies into the Spring Boot fat jar. Jackson doesn't do anything weird with metadata files, classloaders, etc. It plays well with others in a fat jar.
Given that you haven't shared much of your build files, the easiest way to figure out if something has excluded the Jackson XML module is to just run jar -xvf target/app.jar and inspect the output to see if it's in there.
If it's not, look for a Gradle equivalent of the Maven dependency plugin's dependency-tree target that will show you the whole transitive dependency tree. If it's being excluded you'll definitely see it missing from a dependency dump.
I added this artifact which is a war to my gradle project dependencies.
I need to extend some classes, and use a modified servlet contexts from it.
I was expecting the war to be imported as is then I would use gradle tasks to manipulate to include the jars to dependencies, copy static resources to correct classpath etc.
But gradle actually added a bunch of jars to dependency.
Im not sure if gradle scanned recursively all paths for jars and poms or probably just the jars under the WEB-INF/classes folder in the war.
I can assume probably not the poms repositories as stated here.
Im I correct is assuming the jars in the WEB-INF/lib folder in the deflated war were not imported? its hard to tell as there are a lot of shared dependencies between my project and the war in question
Then whats the best way to declare a dependency on a war in the maven repo/jcenter if I need to extend and modify as I described at the top?
UPDATE:
I am now trying to use answer below and this solution https://discuss.gradle.org/t/how-to-add-an-artifactory-war-as-a-gradle-dependency/19804/2
, This only worked after moving the directory with the copied jars outside the buildDir
my build.gradle
configurations {
warOnly
}
dependencies {
// not working implementation fileTree('$buildDir/explodedWar/WEB-INF/classes')
implementation fileTree('anotherDir/explodedWar/WEB-INF/classes')
// implementation fileTree('$buildDir/explodedWar/WEB-INF/lib')
implementation fileTree('anotherDir/explodedWar/WEB-INF/lib')
warOnly 'ca.uhn.hapi.fhir:hapi-fhir-jpaserver-starter:4.2.0#war'
}
tasks.register("explodeWar",Copy) {
from zipTree(configurations.warOnly.singleFile)
// into "${buildDir}/explodedWar"
into "anotherDir/explodedWar"
}
compileJava {
dependsOn explodeWar
}
By declaring a dependency on a WAR, Gradle will simply add it to the list of files for the matching configuration. So if you add a WAR in implementation, it will simply be on the compileClasspath and runtimeClasspath without any processing.
So for sure Gradle will not transform your WAR dependency in a dependency on the JARs it contains.
If you want to use a WAR to copy and modify some of its content before repackaging it, you can use an isolated and custom configuration to resolve it from a remote repositories. Then you will define a Gradle task that will take the files of that configuration as the input and do the required processing on the WAR. Note that the task could also be the starting point of a series of tasks manipulating the WAR to one output, then that output to another one, etc ...
configurations {
warOnly
}
dependencies {
warOnly "com.pany:some-war:1.0"
}
tasks.register("copyWar", Copy) { // Register a copy task to modify the WAR
from(zipTree(configurations.warOnly)) // I did not run this, so you may have to get to the single file instead
// Regular copy configuration to decide a destination, perform on the fly changes, etc ...
}
It took some trail and error but there is better way. When using the dependency:
providedCompile 'ca.uhn.hapi.fhir:hapi-fhir-testpage-overlay:6.0.1'
Gradle will download a war file and place it you your classpath. However providing a classifier will help here. The following dependency will get the jar file.
providedCompile (group: 'ca.uhn.hapi.fhir', name: 'hapi-fhir-testpage-overlay', version:'6.0.1', classifier: 'classes')
I have a java gradle project. I have a dependency.
dependencies {
compile project(":mymodule")
compile 'org.springframework:spring-context:4.1.2.RELEASE'
}
Where can I find and use exact jar file name of both my custom module and spring dependency jar to process it further in composing application's libs, folders, etc?
Stick this in a new task:
project.configurations.compile.each{ println it}
Or, for just one specific dependency:
println project.configurations.compile.find {it.name.startsWith("something") }
However, if you're looking to create distributable packages including dependencies, you really should look into the gradle application plugin.
I've a gradle module called "Service" and module called ProtocolLibrary, generated by Idea. How can I add library module to my main module?
I have tryed to simply add source dir to project:
main {
java.srcDirs = ['src/main/java', '../ProtocolLibrary/src']
resources.srcDirs = ['src/main/resources']
}
But it doen't work:
Error:Can't register given path of type 'SOURCE' because it's out of content root.
If you wish to combine two separate gradle projects together, then you should look at gradle multi-project builds. You will need to create a root project, which will include both your modules as subprojects and in that case, you can simply use one project as the dependency of another, like:
//service project dependencies
dependencies {
compile project(':ProtocolLibrary')
...
}
After doing this, there is no reason to include sources from one project into another.
The second approach is to use the artifact, generated by ProtocolLibrary. You can make a dependency in Service project specifying the jar in filesystem, or your ProtocolLibrary could be published in your maven repo and be used as usual dependency.
I have a gradle project with several subprojects. I have defined several dependencies with version numbers in the root project but not all subprojects use all of these dependencies.
root
- build.gradle
- compile 'math:math:1.0.0'
- settings.gradle
- include 'messages'
- include 'message-handler'
\ messages
- build.gradle
- //no math
\ message-handler
- build.gradle
- compile 'math:math'
Will my artifact of the messages project contain a dependency on the math library?
In other words, if I make a separate project that depends on the messages artifact from a nexus repository, would my dependency tree show the math library for this new project?
Yes - your messages project artifact will contain a dependency on the math library.
According to Gradle Documentation:
For most multi-project builds, there is some configuration which is
common to all projects. In our sample, we will define this common
configuration in the root project, using a technique called
configuration injection. Here, the root project is like a container
and the subprojects method iterates over the elements of this
container - the projects in this instance - and injects the specified
configuration
In other words, every configuration which is included in the root project will hold for all the sub-projects.