classes not visible to gradle task swagger when using kongchen swagger-maven-plugin - gradle

When performing gradle clean and then gradle swagger a ClassNotFoundException is thrown. If gradle swagger is then run again (basically after the api build is done in previous run), it works fine.
build.gradle looks as below:
buildscript {
repositories {
maven { url hydraMavenRepo }
maven { url hydraPluginsRepo }
}
dependencies {
classpath "com.github.kongchen:swagger-maven-plugin:3.1.4"
}
}
apply plugin: 'java'
configurations {
addclasspath
}
dependencies {
addclasspath files(project(':api:desktop-api').configurations['runtime'].files)
addclasspath files(project(':api:desktop-api').sourceSets['main'].output)
addclasspath files(project(':api:desktop-api').sourceSets.main.output.classesDir)
runtime project(':api:desktop-api')
}
sourceSets {
main {
runtimeClasspath += files(project(':api:desktop-api').sourceSets['main'].output)
runtimeClasspath += files(project(':api:desktop-api').sourceSets['main'].output.classesDir)
runtimeClasspath += files(project(':api:desktop-api').configurations['runtime'].files)
}
}
import com.github.kongchen.swagger.docgen.mavenplugin.ApiDocumentMojo
import com.github.kongchen.swagger.docgen.mavenplugin.ApiSource
import io.swagger.models.Info
task swagger(dependsOn: [':api:desktop-api:build']) {
doLast {
logger.info 'Swagger GenDoc...'
project.file(reportsDir).mkdirs()
// a trick to have all needed classes in the classpath
def customClassLoader = new GroovyClassLoader()
buildscript.configurations.classpath.each {
//println it.toURI().toURL()
customClassLoader.addURL(it.toURI().toURL())
}
configurations.addclasspath.each {
customClassLoader.addURL(it.toURI().toURL())
}
// the same settings as in the swagger-maven-example/pom.xml
final ApiDocumentMojo mavenTask = Class.forName('com.github.kongchen.swagger.docgen.mavenplugin.ApiDocumentMojo', true, customClassLoader).newInstance(
apiSources: [
new ApiSource(
springmvc: false,
locations: ['com/vmware/vdi/hydra'],
schemes: ['http', 'https'],
host: 'vmware.com',
basePath: '/api',
info: new Info(
title: "Hydra DS-REST API's",
version: 'v100',
description: "Hydra DS-REST API's",
),
swaggerDirectory: reportsDir
)
]
)
mavenTask.execute()
logger.info 'Swagger GenDoc task is completed'
}
}

You have several flaws in your build script.
You should not depend on built things in build script dependencies. This is a hen and egg problem. You need to execute the build to get the classes that are necessary to execute the build. This cannot work reliably, if at all.
Instead you should declare them as dependencies outside the buildscript block. The buildscript block is only for dependencies that are needed by your build script to run itself, like Gradle Tasks and Gradle Plugins or classes you use during the build, like the swagger-maven-plugin stuff which is correct in the buildscript block.
Besides that, you execute part of your swagger stuff (the instanciation, execution and printing) during the configuration phase instead of during the execution phase. Everything you do in a task closure, but outside any doFirst or doLast blocks is run during the configuration phase, when the build is configured and thus always, no matter what tasks you actually want to execute and no matter whether the task may already be up-to-date or not. For the up-to-date check to work and save your time you need to declare all inputs like files and properties that might change between executions and all outputs that you generate, then Gradle can do its magic to only execute the task when actually necessary.
Also you should not use println in build scripts. That is like using System.out.println in Java programs. Instead you should use the provided logging facility directly, e. g. doing logger.info 'Swagger GenDoc task is completed'.

buildscript.classloader is what I was looking for.
Below is the code that works:
buildscript {
repositories {
maven { url mavenRepo }
}
dependencies {
classpath "com.github.kongchen:swagger-maven-plugin:3.1.4"
}
}
import com.github.kongchen.swagger.docgen.mavenplugin.ApiDocumentMojo
import com.github.kongchen.swagger.docgen.mavenplugin.ApiSource
import io.swagger.models.Info
task swagger(dependsOn: ':api:build') {
doLast {
logger.info 'Swagger GenDoc...'
project.file(<dir>).mkdirs()
FileCollection apiRuntimeFiles = files(project(':api').configurations['runtime'].files)
apiRuntimeFiles.each {
buildscript.classLoader.addURL(it.toURI().toURL())
}
FileCollection apiClassFiles =files(project(':api').sourceSets['main'].output)
apiClassFiles.each {
buildscript.classLoader.addURL(it.toURI().toURL())
}
final ApiDocumentMojo mavenTask = Class.forName('com.github.kongchen.swagger.docgen.mavenplugin.ApiDocumentMojo', true, buildscript.classLoader).newInstance(
apiSources: [
new ApiSource(
springmvc: false,
locations: ['<loc>'],
schemes: ['http', 'https'],
host: '<host>',
basePath: '/api',
info: new Info(
title: "REST API's",
version: 'v1',
description: "REST API's",
),
swaggerDirectory: <dir>
)
]
)
mavenTask.execute()
logger.info 'Swagger GenDoc task is completed'
}
}

Related

Running specific tests using gradle over multiple browsers

I'm using Geb/Spock for automated testing. I'm using Gradle as my build tool.
I'd like to call different gradle tasks to build and run a specific spec(test) or a suite of specs.
I dont know enough about the gradle build lifecycle to completely understand what is going on here: https://github.com/geb/geb-example-gradle/blob/master/build.gradle
plugins {
id "idea"
id "groovy"
id "com.energizedwork.webdriver-binaries" version "1.4"
id "com.energizedwork.idea-base" version "1.4"
}
ext {
// The drivers we want to use
drivers = ["firefox", "chrome", "chromeHeadless"]
ext {
groovyVersion = '2.4.12'
gebVersion = '2.2'
seleniumVersion = '3.6.0'
chromeDriverVersion = '2.32'
geckoDriverVersion = '0.18.0'
}
}
repositories {
mavenCentral()
}
dependencies {
// If using Spock, need to depend on geb-spock
testCompile "org.gebish:geb-spock:$gebVersion"
testCompile("org.spockframework:spock-core:1.1-groovy-2.4") {
exclude group: "org.codehaus.groovy"
}
testCompile "org.codehaus.groovy:groovy-all:$groovyVersion"
// If using JUnit, need to depend on geb-junit (3 or 4)
testCompile "org.gebish:geb-junit4:$gebVersion"
// Drivers
testCompile "org.seleniumhq.selenium:selenium-chrome-driver:$seleniumVersion"
testCompile "org.seleniumhq.selenium:selenium-firefox-driver:$seleniumVersion"
}
webdriverBinaries {
chromedriver chromeDriverVersion
geckodriver geckoDriverVersion
}
drivers.each { driver ->
task "${driver}Test"(type: Test) {
group JavaBasePlugin.VERIFICATION_GROUP
outputs.upToDateWhen { false } // Always run tests
systemProperty "geb.build.reportsDir", reporting.file("geb/$name")
systemProperty "geb.env", driver
}
}
test {
dependsOn drivers.collect { tasks["${it}Test"] }
enabled = false
}
tasks.withType(Test) {
maxHeapSize = "1g"
jvmArgs '-XX:MaxMetaspaceSize=128m'
testLogging {
exceptionFormat = 'full'
}
}
tasks.withType(GroovyCompile) {
groovyOptions.forkOptions.memoryMaximumSize = '256m'
}
I've tried inserting the following into build.gradle:
task dataGen {
include '**com.company.project.spec.util/DataGenerationUtilSpec.groovy'
}
task sanity {
include '**com.company.project.spec.sanity.*'
}
But calling these tasks (gradle sanity) results in a build failure:
Could not find method include() for arguments [**com.company.project.spec.util/DataGenerationUtilSpec.groovy] on task ':dataGen' of type org.gradle.api.DefaultTask
Obviously there's existing build instructions since I can call gradle build and all the specs run on Chrome, I'm just not sure how to add more tasks
I think these 2 tasks are test tasks so it should look like that :
task dataGen (type: Test) {
include '**com.company.project.spec.util/DataGenerationUtilSpec.groovy'
}
task sanity (type: Test) {
include '**com.company.project.spec.sanity.*'
}
You can use Spock annotation to control the test or the Spec, see example here.
You will have to define annotation classes and define the Spock config file to use that annotation. You then annotate the specific Specification (or test).
Now you will have to define the Spock config file in the task or from a parameter.

I got an error using this build.grafle file and don't know how to fix it

Here's the Error:
FAILURE: Build failed with an exception.
Where: Build file '/home/wieland/GitGradlePackaging/build.gradle' line: 22
What went wrong: A problem occurred evaluating root project 'GitGradlePackaging'.
Could not get unknown property 'org' for object of type org.gradle.api.internal.initialization.DefaultScriptHandler.
And Here's my build.gradle File:
/*
* This file was generated by the Gradle 'init' task.
*
* This generated file contains a sample Java project to get you started.
* For more details take a look at the Java Quickstart chapter in the Gradle
* user guide available at https://docs.gradle.org/4.6/userguide/tutorial_java_projects.html
*/
//From example: http://mrhaki.blogspot.co.at/2015/04/gradle-goodness-use-git-commit-id-in.html
buildscript {
repositories {
jcenter()
}
dependencies {
//Add dependencies for build script, so we can access Git from our build script
classpath 'org.ajoberstar:grgit:1.1.0'
}
def git = org.ajoberstar.grgit.Grgit.open(file('.'))
//To save Githash
def githash = git.head().abbreviatedId
}
plugins {
// Apply the java plugin to add support for Java
id 'java'
// Apply the application plugin to add support for building an application
id 'application'
// Apply the groovy plugin to also add support for Groovy (needed for Spock)
id 'groovy'
id 'distribution'
}
// Set version
project.version = mainProjectVersion + " - " + githash
project.ext.set("wholeVersion", "$project.version - $githash")
project.ext.set("buildtimestamp", "2000-01-01 00:00")
def versionfilename = "versioninfo.txt"
def GROUP_DEBUG = 'Debug'
// Task to print project infos
task debugInitialSettings {
group = GROUP_DEBUG
doLast {
println 'Version: ' + project.wholeVersion
println 'Timestamp: ' + project.buildtimestamp
println 'Filename: ' + project.name
}
}
// To add the githash to zip
task renameZip {
doLast {
new File ("$buildDir/distributions/$project.name-${project.version}.zip")
.renameTo ("$buildDir/distributions/$project.name-${project.wholeVersion}.zip")
}
}
distZip.finalizedBy renameZip
// To add the githash to tar
task renameTar{
doLast {
new File ("$buildDir/distributions/$project.name-${project.version}.tar")
.renameTo ("$buildDir/distributions/$project.name-${project.wholeVersion}.tar")
}
}
distTar.finalizedBy renameTar
// Define the main class for the application
mainClassName = 'App'
dependencies {
// This dependency is found on compile classpath of this component and consumers.
compile 'com.google.guava:guava:23.0'
// Use the latest Groovy version for Spock testing
testCompile 'org.codehaus.groovy:groovy-all:2.4.13'
// Use the awesome Spock testing and specification framework even with Java
testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
testCompile 'junit:junit:4.12'
}
// In this section you declare where to find the dependencies of your project
repositories {
// Use jcenter for resolving your dependencies.
// You can declare any Maven/Ivy/file repository here.
jcenter()
}
//To generate Testreports as HTML
test {
reports {
junitXml.enabled = false
html.enabled = true
}
}
distributions {
main {
contents {
from { 'build/docs' }
into ('reports') {
from 'build/reports'
}
}
}
}
//To make sure that test and javadoc ran before zip and tar
distTar.dependsOn test
distZip.dependsOn test
distTar.dependsOn javadoc
distZip.dependsOn javadoc
Please keep in mind I have not much knowledge about gradle as I'm just starting to learn it!
Thanks in advance :)
You have to move the githash definition outside the buildscript block
buildscript {
repositories {
jcenter()
}
dependencies {
//Add dependencies for build script, so we can access Git from our build script
classpath 'org.ajoberstar:grgit:1.1.0'
}
}
def git = org.ajoberstar.grgit.Grgit.open(file('.'))
//To save Githash
def githash = git.head().abbreviatedId
The reason is that when the buildscript block is evaluated line by line, its dependencies are not yet loaded. When the rest of the script is evaluated, the dependencies of the buildscript block have already been loaded. This is actually the reason for the buildscript block existence: to be run before the rest of the build and prepare the setup.

How to apply javaagent to gretty plugin based on gradle command line?

The question is specific, but it's more of a general 'how to do this in gradle' question.
I have a demo java web app that I can run using the gretty plugin. I would like to selectively control whether a javaagent is applied to the jvmArgs of the gretty process based on a command line flag. The agent jar location is known by getting its path from a dummy configuration:
configurations {
agent
}
dependencies {
...
agent group: 'com.foo', name: 'foo-agent', version: '1.0'
}
I know I can access the jar file location using something like:
project.configurations.agent.find { it.name.startsWith("foo-agent") }
How can I selectively apply that to the gretty jvmArgs configuration based on a command line property such as
gradle -PenableAgent
I ended up solving this by creating a task and simply calling it before I run the war:
task agent {
doFirst {
def agentJar = project.configurations.agent.find { it.name.startsWith("foo-agent") }
gretty.jvmArgs << "-javaagent:" + agentJar
}
}
Then I can simply call:
gradle agent appRunWar
In my project I use Spring Instrument as java agent so this was my solution.
You can make appRun task dependent on agent task then no additional gradle run parameter needed.
dependencies {
...
agent 'org.springframework:spring-instrument:4.2.4.RELEASE'
}
configurations {
dev
agent
}
gretty {
...
contextPath = '/'
jvmArgs=[]
springBoot = true
...
}
task agent {
doFirst {
def agentJar = project.configurations.agent.find{it.name.contains("spring-instrument") }
gretty.jvmArgs << "-javaagent:" + agentJar
}
}
project.afterEvaluate {
tasks.appRun.dependsOn agent
}

How to run JBoss TattleTale from inside Gradle build

I am in love with JBoss TattleTale. Typically, in my Ant builds, I follow the docs to define the Tattletale tasks and then run them like so:
<taskdef name="report"
classname="org.jboss.tattletale.ant.ReportTask"
classpathref="tattletale.lib.path.id"/>
...
<tattletale:report source="${src.dir]" destination="${dest.dir}"/>
I am now converting my builds over to Gradle and am struggling to figure out how to get Tattletale running in Gradle. There doesn't appear to be a Gradle-Tattletale plugin, and I'm not experienced enough with Gradle to contribute one. But I also know that Gradle can run any Ant plugin and can also executing stuff from the system shell; I'm just not sure how to do this in Gradle because there aren't any docs on this (yet).
So I ask: How do I run the Tattletale ReportTask from inside a Gradle build?
Update
Here is what the Gradle/Ant docs show as an example:
task loadfile << {
def files = file('../antLoadfileResources').listFiles().sort()
files.each { File file ->
if (file.isFile()) {
ant.loadfile(srcFile: file, property: file.name)
println " *** $file.name ***"
println "${ant.properties[file.name]}"
}
}
}
However, no where in here do I see how/where to customize this for Tattletale and its ReportTask.
The following is adapted from https://github.com/roguePanda/tycho-gen/blob/master/build.gradle
It bypasses ant and directly invokes the Tattletale Java class.
It was changed to process a WAR, and mandates a newer javassist in order to handle Java 8 features such as lambdas.
configurations {
tattletale
}
configurations.tattletale {
resolutionStrategy {
force 'org.javassist:javassist:3.20.0-GA'
}
}
dependencies {
// other dependencies here...
tattletale "org.jboss.tattletale:tattletale:1.2.0.Beta2"
}
task createTattletaleProperties {
ext.props = [reports:"*", enableDot:"true"]
ext.destFile = new File(buildDir, "tattletale.properties")
inputs.properties props
outputs.file destFile
doLast {
def properties = new Properties()
properties.putAll(props)
destFile.withOutputStream { os ->
properties.store(os, null)
}
}
}
task tattletale(type: JavaExec, dependsOn: [createTattletaleProperties, war]) {
ext.outputDir = new File(buildDir, "reports/tattletale")
outputs.dir outputDir
inputs.files configurations.runtime.files
inputs.file war.archivePath
doFirst {
outputDir.mkdirs()
}
main = "org.jboss.tattletale.Main"
classpath = configurations.tattletale
systemProperties "jboss-tattletale.properties": createTattletaleProperties.destFile
args([configurations.runtime.files, war.archivePath].flatten().join("#"))
args outputDir
}
The previous answers either are incomplete or excessively complicated. What I did was use the ant task from gradle which works fine. Let's assume your tattletale jars are beneath rootDir/tools/...
ant.taskdef(name: "tattleTaleTask", classname: "org.jboss.tattletale.ant.ReportTask", classpath: "${rootDir}/tools/tattletale-1.1.2.Final/tattletale-ant.jar:${rootDir}/tools/tattletale-1.1.2.Final/tattletale.jar:${rootDir}/tools/tattletale-1.1.2.Final/javassist.jar")
sources = "./src:./src2:./etcetera"
ant.tattleTaleTask(
source: sources,
destination: "tattleTaleReport",
classloader: "org.jboss.tattletale.reporting.classloader.NoopClassLoaderStructure",
profiles: "java5, java6",
reports: "*",
excludes: "notthisjar.jar,notthisjareither.jar,etcetera.jar"
){
}
So the above code will generate the report beneath ./tattleTaleReport. It's that simple. The annoyance is that the source variable only accepts directories so if there are jars present in those directories you do not wish to scan you need to add them to the excludes parameter.

Grails gradle "a task with that name already exists"

I'm trying to create a test task rule using the example provided in the grails gradle doc but I keep getting "a task with that name already exists" error.
My build script is as follows:
import org.grails.gradle.plugin.tasks.* //Added import here else fails with "Could not find property GrailsTestTask"
buildscript {
repositories {
jcenter()
}
dependencies {
classpath "org.grails:grails-gradle-plugin:2.0.0"
}
}
version "0.1"
group "example"
apply plugin: "grails"
repositories {
grails.central() //creates a maven repo for the Grails Central repository (Core libraries and plugins)
}
grails {
grailsVersion = '2.3.5'
groovyVersion = '2.1.9'
springLoadedVersion '1.1.3'
}
dependencies {
bootstrap "org.grails.plugins:tomcat:7.0.50" // No container is deployed by default, so add this
compile 'org.grails.plugins:resources:1.2' // Just an example of adding a Grails plugin
}
project.tasks.addRule('Pattern: grails-test-app-<phase>') { String taskName ->
println tasks //shows grails-test-app-xxxxx task. Why?
//if (taskName.startsWith('grails-test-app') && taskName != 'grails-test-app') {
// task(taskName, type: GrailsTestTask) {
// String testPhase = (taskName - 'grails-test-app').toLowerCase()
// phases = [testPhase]
// }
//}
}
Running $gradle grails-test-integration
or in fact anything of the form $gradle grails-test-app-xxxxxxxx yields the error "Cannot add task 'gradle grails-test-app-xxxxxxxx as a task with that name already exists".
Can someone please advise how I can resolve this error? Thanks.
If you don't mind overriding the task created by the plugin, you might want to try
task(taskName, type: GrailsTestTask, overwrite: true)
In general, when using task rules that can be called multiple times (for instance if you have multiple tasks depending on a task eventually added by your rules), I use the following test before actually creating the task:
if (tasks.findByPath(taskName) == null) {tasks.create(taskName)}
This will call the task() constructor only if this task name does not exists.

Resources