How do I create Java Gradle Applications with different resources - java

I know this should be pretty simple, but there's obviously something that I'm just not getting.
I have a gradle application setup in the following in build.gradle:
plugins {
id 'application'
}
application {
mainClass = 'com.my.Main'
}
dependencies {
implementation 'other project 1'
implementation 'other project 2'
}
The source is set up
src/
main/
java/
resources/
I want to have a different distribution (for local development) that has different files for the resources directory, but for this 'dev' distribution, I want to keep the mainClass and the dependencies from the main distribution.
If I add a 'dev' distribution, I can package up files from the src/dev/... directory, but I don't have the application dependencies.
distributions {
dev {
contents {
// something here to add the compiled java and dependencies from main
// but not the resources?
}
}
main {
}
}
Also, gradle only seems to have one 'run' task that runs the main distribution. Is it possible to define a run for the dev distribution?

You need to define a SourceSet called dev:
sourceSets {
main {
}
dev {
resources {
exclude ...
}
}
}
And also the corresponding directory (that's not even required, when only excluding files):
mkdir -p ./modulename/src/dev/resources

Related

How to add OpenAPI client as a subproject?

I can successfully add a generated openapi client to my project via source sets. But then I have to copy dependencies into the main build-gradle, resolve conflicts -> I think it would be a better design to have the client as a subproject with its own build.gradle.
So I add include = 'build:openapi-java-client' to my settings.gradle and compile project(':build:openapi-java-client') to my dependencies. So that I have the following files:
build.gradle:
plugins {
id 'java'
id 'application'
id "org.openapi.generator" version "4.3.1"
}
repositories {
jcenter()
}
openApiGenerate {
generatorName = "java"
inputSpec = "$rootDir/specs/petstore.yaml".toString()
outputDir = "$buildDir/openapi-java-client".toString()
apiPackage = "org.openapi.example.api"
invokerPackage = "org.openapi.example.invoker"
modelPackage = "org.openapi.example.model"
configOptions = [
dateLibrary: "java8"
]
}
dependencies {
implementation 'com.google.guava:guava:29.0-jre'
testImplementation 'junit:junit:4.13'
compile project(':build:openapi-java-client')
}
application {
mainClassName = 'a.aa.App'
}
and settings.gradle:
rootProject.name = 'simple-java-app'
include = 'build:openapi-java-client'
I execute openApiGenerate in advance, after adding it as a subproject, I do Gradle -> Refresh Gradle Project and Refresh.
Eclipse then shows me a problem:
Could not run phased build action using Gradle distribution 'https://services.gradle.org/distributions/gradle-6.5.1-bin.zip'.
Settings file 'C:\...\simple-java-app\settings.gradle' line: 11
A problem occurred evaluating settings 'simple-java-app'.
Could not set unknown property 'include' for settings 'simple-java-app' of type org.gradle.initialization.DefaultSettings.
I don't know where to go from here, addressing subprojects in subfolders worked just fine when I worked through https://guides.gradle.org/creating-multi-project-builds/ and put greeting-library in a subfolder.
You are trying to make build/ a project when that directory specifically is not meant to be a project directory. It's Gradle default build directory and likely 99% of other plugins and other Gradle plugins.
Simply change output directory to something else other than build/:
openApiGenerate {
generatorName.set("java")
inputSpec.set("$rootDir/specs/petstore.json")
outputDir.set("$rootDir/openapi-java-client")
apiPackage.set("org.openapi.example.api")
invokerPackage.set("org.openapi.example.invoker")
modelPackage.set("org.openapi.example.model")
}
Then include the project in your build with the correct syntax:
// settings.gradle
include("openapi-java-client")
However, using the org.openapi.generator seems to generate an invalid build.gradle since I get the following error:
FAILURE: Build failed with an exception.
* Where:
Build file 'C:\Users\fmate\code\example\openapi-java-client\build.gradle' line: 23
* What went wrong:
Could not compile build file 'C:\Users\fmate\code\example\openapi-java-client\build.gradle'.
> startup failed:
build file 'C:\Users\fmate\code\example\openapi-java-client\build.gradle': 23: unexpected char: '\' # line 23, column 35.
main.java.srcDirs = ['src/main\java']
This obviously won't work how you wanted it to since it appears to be an issue with the Gradle plugin itself. If you just need to include the generate code in your project, then just include the generated Java code as part of your main Java source:
openApiGenerate {
generatorName.set("java")
inputSpec.set("$rootDir/specs/petstore.json")
outputDir.set("$buildDir/openapi-java-client")
apiPackage.set("org.openapi.example.api")
invokerPackage.set("org.openapi.example.invoker")
modelPackage.set("org.openapi.example.model")
}
tasks {
compileJava {
dependsOn(openApiGenerate)
}
}
sourceSets {
main {
java {
srcDir(files("${openApiGenerate.outputDir.get()}/src/main"))
}
}
}
But with this approach, you'll run into missing imports/dependencies. It doesn't appear this plugin offers the ability to just generate the models/POJOs only, so updating the library property to native and including some missing dependencies manually, it all works:
plugins {
java
id("org.openapi.generator") version "5.0.0-beta"
}
repositories {
mavenCentral()
}
group = "io.mateo.test"
dependencies {
implementation(platform("com.fasterxml.jackson:jackson-bom:2.11.1"))
implementation("com.fasterxml.jackson.core:jackson-databind")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
implementation("org.openapitools:jackson-databind-nullable:0.2.1")
implementation("com.google.code.findbugs:jsr305:3.0.2")
implementation("io.swagger:swagger-core:1.6.2")
}
openApiGenerate {
generatorName.set("java")
inputSpec.set("$rootDir/specs/petstore.json")
outputDir.set("$buildDir/openapi-java-client")
apiPackage.set("org.openapi.example.api")
invokerPackage.set("org.openapi.example.invoker")
modelPackage.set("org.openapi.example.model")
library.set("native")
configOptions.put("dateLibrary", "java8")
}
tasks {
compileJava {
dependsOn(openApiGenerate)
}
}
sourceSets {
main {
java {
srcDir(files("${openApiGenerate.outputDir.get()}/src/main"))
}
}
}
You cannot configure it alike this, because build most certainly is an output directory, which would create a circular reference. Better try to add a new module and add that generator plugin into that module. If you can configure another module as outputDir, this could be referenced.
Even if the plugin resides in the root project, the destination needs to be a module.
The point is, that the root project always executes, opposite to module configutions.
I’ve just answered a very similar question. While my answer there is not perfect, I would personally still prefer the approach suggested there – and kind of repeated here:
Suggested Approach
I would keep the builds of the modules that depend on the generated API completely separate from the build that generates the API. The only connection between such builds should be a dependency declaration. That means, you’ll have to manually make sure to build the API generating project first and only build the dependent projects afterwards.
By default, this would mean to also publish the API module before the dependent projects can be built. An alternative to this default would be Gradle composite builds – for example, to allow you to test a newly generated API locally first before publishing it. However, before creating/running the composite build, you would have to manually run the API generating build each time that the OpenAPI document changes.
Example
Let’s say you have project A depending on the generated API. Its Gradle build would contain something like this:
dependencies {
implementation 'com.example:api:1.0'
}
Of course, the simple-java-app build described in the question would have to be adapted to produce a module with these coordinates:
openApiGenerate {
// …
groupId = "com.example"
id = "api"
version = "1.0"
}
Before running A’s build, you’d first have to run
./gradlew openApiGenerate from your simple-java-app project.
./gradlew publish from the simple-java-app/build/openapi-java-client/ directory.
Then A’s build could fetch the published dependency from the publishing repository.
Alternatively, you could drop step 2 locally and run A’s build with an additional Gradle CLI option:
./gradlew --include-build $path_to/simple-java-app/build/openapi-java-client/ …

How to build distribution package in multi-module Gradle project?

I have seen this post Gradle multi project distribution but still have some doubts.
I would like to create the following project layout
root
|--lib-java-module
|--spring-boot-module
|--3PP_A_module # not java
| |-- custom scripts, config
|--3PP_B_module # not java
| |-- custom scripts, config
|--dist-module
As you might have guessed, I want the dist-module to build myapp-dist.tar.gz with libjava.jar, sprintbootapp.jar, 3pp-a.tar, 3pp-b.tar.
myapp-dist.tar.gz
libjava.jar
sprintbootapp.jar
3pp-a.tar
3pp-b.tar.
The 3pp-a-module and the 3pp-b-module only contain some configuration files and startup scripts. No java or any compiled code. How to package them individually into tar files (no compression)?
How to define dependencies in dist-module to the other modules? Is it possible to get the other modules built when build is triggered from dist-module?
Update:
I setup my test project based on #marco-r's answer and it works except for packaging the war file. Checkout the test project from github https://github.com/KiranMohan/study-spring-boot.
This is the project setup of interest.
include ':sb-2.1-multi-package', ':sb-2.1-multi-package:hello-rest-lib',
':sb-2.1-multi-package:hello-rest-standalone-jar',
':sb-2.1-multi-package:hello-rest-war'
include 'sb-2.1-3pp-resources'
include 'sb-2.1-build'
However adding hello-rest-war to sb-2.1-build.tar.gz fails.
Instead of war files, its the dependencies that are getting packaged.
dependencies {
archivesDeps project(path: ':sb-2.1-3pp-resources', configuration: 'archives')
javaDeps project(":sb-2.1-multi-package:hello-rest-war")
}
...
task copyJavaDeps(type: Copy) {
inputs.files(configurations.javaDeps)
from configurations.javaDeps
into "${ARCHIVE_DIRECTORY}/lib"
}
...
// create distribution bundle
distributions {
main {
contents {
from ARCHIVE_DIRECTORY
into "/springapp/multimodule"
}
}
}
Contents of the package
springapp/multimodule/lib/classmate-1.4.0.jar
springapp/multimodule/lib/hello-rest-lib-0.0.1-SNAPSHOT.jar
springapp/multimodule/lib/hibernate-validator-6.0.16.Final.jar
...
springapp/multimodule/lib/tomcat-embed-websocket-9.0.17.jar
springapp/multimodule/lib/validation-api-2.0.1.Final.jar
springapp/multimodule/sb-2.1-3pp-resources/config/3pp.json
How to package war file (hello-rest-war module) and without all the transitive dependencies?
This is multiple question scenario, so I am going to address it in parts.
Since all 3PP_X_module have the same building requirements create a build.gradle in each of the submodules that refer to an actual build gradle that have the common functionality required:
apply from: '../tarArtifact.gradle'
In the parent folder create the previously referred tarArtifact.gradle to have the functionality to TAR the contents of a subfolder (arbitrarily chosen as contents) of a referring subproject:
apply plugin: 'base'
task tarContents(type: Tar) {
from 'contents'
archiveName = "${project.name}.tar"
destinationDir file('build/tar')
}
artifacts {
archives file: tarContents.archivePath, type: 'tar', builtBy: tarContents
}
Since the archives configuration is wired to the output of the tarContents (builtBy: tarContents), then the archives configuration can be used to retrieve the desired TAR as the output of building this project naturally.
Create in dist-module the following build.gradle file:
apply plugin: 'distribution'
plugins.withType(DistributionPlugin) {
distTar {
compression = Compression.GZIP
extension = 'tar.gz'
}
}
configurations {
wholeProjectDist
}
dependencies {
wholeProjectDist project(path: ':3pp-a-module', configuration: 'archives')
wholeProjectDist project(path: ':3pp-b-module', configuration: 'archives')
wholeProjectDist project(':lib-java-module')
wholeProjectDist project(':spring-boot-module')
}
distributions {
main {
contents {
from configurations.wholeProjectDist
}
}
}
This gradle file includes the following:
Applies the Distribution plugin, so we can generate the final tar.gz file from the artifacts generated by all the other subprojects.
Configures the distTar task (of the DistributionPlugin plugin) to compress any generated TAR using it by using GZIP.
Creates the configuration wholeProjectDist to capture the dependencies of dist-module itself; which we will use with the distribution plugin's tasks.
Declares the dependencies of dist-module as the artifacts output by the siblings' subprojects; using the newly created wholeProjectDist.
Configures the distribution's plugin main configuration to have as contents all the files from configurations.wholeProjectDist
Create a settings.gradle file under dist-module to allow it to access its siblings modules using includeFlat:
includeFlat '3pp-a-module', '3pp-b-module', 'lib-java-module', 'spring-boot-module'
Include in the parent folder a settings.gradle file to include all children submodules (as the root project):
includeFlat '3pp-a-module', '3pp-b-module', 'lib-java-module', 'spring-boot-module'
Build the desired tar.gz files by invoking the gradle command (from the root folder):
gradle :dist-module:distTar
Hope this helps.

Migrating from ant to gradle - macrodef alternatives?

We are migrating from Ant to Gradle. In the starting Gradle looked promising, when I started migrating targets from Ant to Gradle, facing similar issues, earlier having with Ant.
The Ant Build Flow looks like -
We have a Java application - Ant used to clean, compile and build jar of the project.
Next we have few targets to copy built jar and some other resources files to windows server.Its just a windows server not a nexus like repo.
These copy targets are release based(internally using macrodefs) - if alpha, it has to copy to different folder and for stable release different folder.
Another macrodef was to just backup the existing files present in server to a backup folder before copying new files there.
Migrating to Gradle-
I liked the way Gradle does the tasks of clean, compile and build. I am using Java plugin for this.
I am facing issues in next tasks to copy files to windows server and taking backup of old files before copying new files.
In Gradle I created few custom tasks like copyJarAlpha, copyJarStable,backupJarAlpha,backupJarStable etc..
Internally they are doing the same thing just copying files from source to destination and some print statements. But it looks repetitive. I was looking for ways like macrodef in ant.
task deployAlpha {
doFirst {
isJenkinsAvailable(deployDirAlpha)
}
doFirst {
if(isDeployLibsTaskRequired(outputDir,deployDirAlpha)) {
tasks.findByName('deployLibsAlpha').execute()
}
}
doLast {
println "Deploying corejar jar to : ${deployDirAlpha}"
copy {
from "${outputDir}/${corejar}"
into "${deployDirAlpha}"
}
println "Deploying evan-core jar to : ${deployDirAlpha}/lib"
copy {
from "${externalLibsDir}/core2.jar"
into "${deployDirAlpha}/lib"
}
println "Deploying test resource files to : ${deployDirAlpha}"
copy {
from "./test-resources/logback-test.xml", "./test-resources/JUnit-TestSuiteResults.xsl"
into "${deployDirAlpha}"
}
}
}
I have similar snippets at few places. Its not looking nice.
Is there any way we can reuse methods/functions in gradle? or I am assuming wrong things here.. Or I am trying to use gradle for things its not inteded for? What could be the best way to accomplish these tasks of copying and backing up files if not using custom tasks in gradle?
Here's a couple of options.
Create a plugin (See writing custom plugins)
public class MyPlugin implements Plugin<Project> {
void apply(Project project) {
project.with {
apply plugin: 'x'
dependencies {...}
task deployAlpha{...}
}
}
}
Usage
apply plugin: MyPlugin
Or a script plugin
apply from: 'path/to/script.gradle'
You can also attach functions to domain objects via the ext of a domain object (see ExtraPropertiesExtension)
allprojects {
ext.doProjectStuff = { String arg -> return "project${arg}"}
dependencies.ext.doSependenciesStuff = { String arg -> return "dependencies${arg}"}
}
Usage
def foo = doProjectStuff('x')
dependencies {
def bar = doDependenciesStuff('y')
}

Choose Directory for Gradle Generated Source Code

I'm using Dagger 2 to generate some source code in my Gradle project. Right now those sources are being generated and added in the ./build/classes/main folder along with all the class files.
How do I choose a folder to separate all the generated .java files to?
How do I include that folder in my gradle Java project, and have IntelliJ view those as sources so I can use them in my project?
It looks like the application plugin only uses a certain set of directories by default, mixing in flavours of build to decide what files to compile.
However, I did find an example build script that creates a dagger configuration and manipulates gradle into using it for the generated output and adds it to the classpath. It uses dagger-compiler.
The core of it is:
sourceSets {
dagger {
java {
srcDirs = ['src/dagger/java']
}
}
}
configurations {
compileDagger
}
compileJava {
description = "dagger annotation processor is loaded automatically from classpath"
sourceSets.dagger.java.srcDirs*.mkdirs()
classpath += configurations.compileDagger
options.compilerArgs += [
'-s', sourceSets.dagger.java.srcDirs.iterator().next()
]
}
clean {
description = "delete files in generated source directory tree"
delete fileTree(dir: sourceSets.dagger.java.srcDirs.iterator().next())
}
dependencies {
ext.daggerVersion = "2.0.1"
compile(
"com.google.dagger:dagger:${daggerVersion}",
"com.google.guava:guava:18.0")
compileDagger(
"com.google.dagger:dagger-compiler:${daggerVersion}")
}
Regarding IntelliJ, the plugin should automatically add any srcSets via the normal building of the idea project, so there should be no additional configuration needed, just regenerate it.

gradle: compile android junit test apk file

Can I compile Android JUnit test apk file by using gradle script? Now my test class is:
public class main extends ActivityInstrumentationTestCase2<LoginWindow> {
public main() {
super("com.tecomgroup.handifox", LoginWindow.class);
}
...
}
and gradle says that he cannot find class LoginWindow. Should I add the LoginWindow.java to dependencies {} block? Will such test work? Or may be there is another way to compile test apk file?
When using Gradle Android plugin, you no longer need to have a separate project for testing. Production sources should be put into src/main/java directory, test sources should be in src/instrumentTest/java. The same applies to resources.
From Android Gradle plugin User Guide on project structure.
Project Structure
The basic build files above expect a default folder structure. Gradle follows the concept of convention over configuration, providing sensible default option values when possible.
The basic project starts with two components called “source sets”. The main source code and the test code. These live respectively in:
src/main/
src/instrumentTest/
Inside each of these folders exists folder for each source components.
For both the Java and Android plugin, the location of the Java source code and the Java resources:
java/
resources/
For the Android plugin, extra files and folders specific to Android:
AndroidManifest.xml
res/
assets/
aidl/
rs/
jni/
Note: src/instrumentTest/AndroidManifest.xml is not needed as it is created automatically.
You can change the standard project layout
sourceSets {
instrumentTest {
java {
srcDir '../other/src/java'
}
resources {
srcDir '../other/src/resources'
}
}
}

Categories

Resources