Logback configuration in Groovy and different class paths in Gradle/Idea - groovy

I am using SLF4J and Logback for logging in a Groovy application, therefore I also configure logback via Groovy configuration. My config is very easy and looks like:
import static ch.qos.logback.classic.Level.INFO
import static ch.qos.logback.classic.Level.DEBUG
import ch.qos.logback.classic.encoder.PatternLayoutEncoder
import ch.qos.logback.core.ConsoleAppender
import ch.qos.logback.core.status.OnConsoleStatusListener
// always a good idea to add an on console status listener
statusListener(OnConsoleStatusListener)
// setting up appenders
appender('CONSOLE', ConsoleAppender) {
encoder(PatternLayoutEncoder) {
pattern = "%d [%thread] %-5level %logger - %msg%n"
}
}
// setting up loggers
logger("org.apache", INFO)
root(DEBUG, ['CONSOLE'])
However, I came into the problem that org.apache logging messages also occur on debug level which shouldn't be according to the config above (I also tried to change the order of looger and root). I found out that the config isn't loaded at all when running the app from IDEA. But when I start it via command line (just executing gradlew run) everything works.
Question 1: Does anyone know what happens here? It seems that the class path started from IDEA is different than the class path started from gradlew and that there is another logback config file available. But the classpath in IDEA is only java and the gradle dependencies. In my opionion (just read it from here) logback should look for a logback.groovy at first and I am quite sure that my file is the only one.
Then I tried to translage a working XML config to Groovy via the online translator. The config is just the same but I noticed that the logback debug option gets lost:
<configuration debug="true">
...
</configuration>
Question 2: Does anyone know how to enable logback debug via Groovy? (debug = true is NOT working)
The full Gradle build file:
// Plugins
apply plugin: 'groovy'
apply plugin: 'application'
apply plugin: 'idea'
// Dependencies
configure(allprojects)
{
ext.groovy = '2.1.0'
ext.slf4jVersion = '1.7.2'
ext.logbackVersion = '1.0.9'
ext.apacheFluentHc = '4.2.3'
}
repositories {
mavenCentral()
}
configurations {
compile.exclude module: 'commons-logging'
}
dependencies {
// Groovy
compile "org.codehaus.groovy:groovy-all:${groovy}:indy"
// Logging
compile "org.slf4j:slf4j-api:$slf4jVersion"
compile "ch.qos.logback:logback-classic:$logbackVersion"
compile "org.slf4j:jcl-over-slf4j:$slf4jVersion"
// Apache HttpClient
compile "org.apache.httpcomponents:fluent-hc:$apacheFluentHc"
}
// Java options
sourceCompatibility = 1.7
targetCompatibility = 1.7
mainClassName = 'XXX'
// Groovy options
[compileGroovy.groovyOptions, compileTestGroovy.groovyOptions]*.with {
fork = true
optimizationOptions = [ indy: true, 'int': false]
encoding = 'UTF-8'
}
// Tasks
task wrap(type:Wrapper, description:"create a gradlew") {
gradleVersion = '1.4'
}

I solved question 1, logback.groovy just wasn't found and therefore a default config was loaded.
When I executed the application in IDEA, I did just run the mainclass and not gradle run. I am used to this from Eclipse, where the output path from Maven and Eclipse are ident. But this is not the case with IDEA and Gradle. While the output path for IDEA is out\production\<project>, it is build\classes\main for Gradle and IDEA does not copy the resources to it's output path.
Now I use gradle run within IDEA and therefore bypass the IDEA build.
Question 2 is just not implemented (Logback 1.0.11), there is no DSL for debug, see code.

Related

Pitest is failing showing: No mutations found due to the supplied classpath or filters + Gradle

I'm trying to run a pitest report on a gradle + kotlin project, but I get the following error:
Exception in thread "main" org.pitest.help.PitHelpError: No mutations found. This probably means there is an issue with either the supplied classpath or filters.
See http://pitest.org for more details.
at org.pitest.mutationtest.tooling.MutationCoverage.checkMutationsFound(MutationCoverage.java:352)
at org.pitest.mutationtest.tooling.MutationCoverage.runReport(MutationCoverage.java:132)
at org.pitest.mutationtest.tooling.EntryPoint.execute(EntryPoint.java:123)
at org.pitest.mutationtest.tooling.EntryPoint.execute(EntryPoint.java:54)
at org.pitest.mutationtest.commandline.MutationCoverageReport.runReport(MutationCoverageReport.java:98)
at org.pitest.mutationtest.commandline.MutationCoverageReport.main(MutationCoverageReport.java:45)
I tried everything that I found on google but still not working for me:
This is my build.gradle config
plugins {
id 'groovy-gradle-plugin'
id 'info.solidsoft.pitest' version '1.7.4'
}
repositories {
maven { url "https://plugins.gradle.org/m2/" }
gradlePluginPortal()
}
dependencies {
implementation 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.20'
implementation 'com.github.jengelman.gradle.plugins:shadow:6.1.0'
}
pitest {
targetClasses = ['com.project.root.to.test.with.pitest.src*'] //by default
"${project.group}.*"
pitestVersion = '1.7.4' //not needed when a default PIT version should be used
threads = 4
outputFormats = ['XML', 'HTML']
timestampedReports = false
}
I tried this targetClasses in a different ways:
targetClasses = ['com.project.root.to.test.with.pitest.src.*'] //by default
targetClasses = ['com/project/root/to/test/with/pitest/src*'] //by default
Can someone help me, please?
You look to be trying to supply pitest with a source folder
com.project.root.to.test.with.pitest.src.
Pitest works against the compiled bytecode, not the source files. It expects
a glob that matches against the package.
com.example.*
I've experienced this same issue today. You'll need to make sure all references to pitest use the same version 1.7.4. This includes
plugin: id 'info.solidsoft.pitest' version '1.7.4'
pitestVersion: pitestVersion.set('1.7.4')
dependency: testCompile
'info.solidsoft.gradle.pitest:gradle-pitest-plugin:1.7.4'
Which out changing all references, then it will break.

Spring boot + Groovy + logback.groovy

I am mixing Groovy and Java in my Spring-boot application. Rest controllers and data access is written in Groovy. Configurations are mainly in Java.
As per logback documentation, if there is a logback.groovy file in the classpath, it's supposed to be picked ahead of logback.xml. However only logback.xml is working in my case.
I am running the app as sprint-boot-application.
Also, it's worth noting that spring suggest to inherit some of the logging configuration like shown below
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<logger name="org.springframework.web" level="DEBUG"/>
</configuration>
There is no way to do this in Groovy config.
build.gradle:
dependencies {
compile("org.springframework.boot:spring-boot-starter-web")
compile("org.springframework:spring-jdbc")
compile("com.h2database:h2")
compile("org.hsqldb:hsqldb")
testCompile("junit:junit")
compile('org.codehaus.groovy:groovy-all:2.3.10')
testCompile('org.codehaus.groovy.modules.http-builder:http-builder:0.5.0-RC2')
compile('org.slf4j:slf4j-simple:1.6.1')
}
sourceSets {
main {
groovy {
srcDirs = ['src/main/groovy', 'src/main/java']
}
java {
srcDirs = []
}
}
test {
groovy {
srcDirs = ['src/test/groovy', 'src/test/java']
}
java {
srcDirs = []
}
}
}
First, your build.gradle looks strange to me:
you don't include the spring-boot-gradle-plugin
in your sourceSets options you define settings which are the default values of the Groovy plugin, see Project layout
Note: even if you mix java and groovy files you don't have to separate them (you can if you want). I usally keep them both in the groovy directory.
in your dependencies section you are using simple dependencies instead of Spring Boot starters (see also the reference doc)
You have 2 DB dependencies (H2 and HSQL)
Try to create a sample project with Spring Initializr - switch to full version. Your build.gradle would look like
buildscript {
ext {
springBootVersion = '1.5.1.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'groovy'
apply plugin: 'org.springframework.boot'
jar {
baseName = 'demo'
version = '0.0.1-SNAPSHOT'
}
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile 'org.springframework.boot:spring-boot-starter'
compile 'org.springframework.boot:spring-boot-starter-logging'
compile 'org.springframework.boot:spring-boot-starter-jdbc'
compile 'org.codehaus.groovy:groovy'
compile 'com.h2database:h2'
testCompile 'org.springframework.boot:spring-boot-starter-test'
testCompile 'org.codehaus.groovy.modules.http-builder:http-builder:0.5.0-RC2'
}
With this configuration logback.groovy should work. For specific problems just post your logback.groovy. But as you have noted, the Groovy config is not a full citizen. When you include the spring-boot-starter-logging starter you can also extend the standard logging config with logback-spring.groovy or logback-spring.xml.
For full control you have to use the XML config and for small projects I stopped using logback.groovy and instead just config the logging starter via some settings in the application.properties, see Custom log configuration.
E.g. some settings for application.properties with logs with colored columns (all platforms except windows < 10 and in IDEA even under windows < 10):
logging.file = logs/jira.log
spring.output.ansi.enabled = DETECT
logging.level.root = INFO
logging.level.org.apache.http = WARN

Create a Groovy executable jar with Spock test set as to be executed

I want to create jar with two groovy files, AppLogic.groovy which consists of two few groovy classes and another file, AppSpec that has Spock test suite and I would like to have this Spock class executed (set as executable). How can I create such jar with all dependencies? I found sth similar for jUnit here: how to export (JUnit) test suite as executable jar but could not adapt it for my needs.
I use gradle for build, here is my build.gradle file:
group 'someGroup'
version '1.0'
apply plugin: 'groovy'
apply plugin: 'java'
apply plugin:'application'
sourceCompatibility = 1.7
repositories {
//some repos here
maven { url "http://repo.maven.apache.org/maven2" }
}
dependencies {
//some dependencies here
}
I was browsing around and found SpockRuntime, but I do not know if and how I can use it to achive my goal.
And the winner is:
static void main(String[] args) {
EmbeddedSpecRunner embeddedSpecRunner = new EmbeddedSpecRunner()
embeddedSpecRunner.runClass(MySpec)
}
I do not advise using the EmbeddedSpecRunner from spock implementation as described in accepted answer.
This is what I found to work reliably with gradle 4.9. The basic approach is to use:
The gradle application plugin to create a single tarfile with all testRuntimeClasspath dependencies and shell scripts to run the spock tests
The gradle maven-publish plugin to publish the tar file as an artifact to your maven repo (in my case nexus)
The build.gradle file looks like this:
apply plugin: 'java'
apply plugin: 'groovy'
apply plugin: 'maven-publish'
apply plugin: 'application'
mainClassName = 'org.junit.runner.JUnitCore' // The junit 4 test runner class
applicationName = 'run-tests-cli' // Feel free to change
repositories {
...
}
dependencies {
...
testImplementation "org.codehaus.groovy:groovy-all:${groovyVersion}"
testImplementation "org.spockframework:spock-core:${spockVersion}"
}
// Package compiled spock / junit tests to <artifact>-test-<version>.jar
task testJar(type: Jar) {
classifier = 'tests'
from sourceSets.test.output.classesDirs
}
// Copy all testRuntimeClasspath dependencies to libs folder
task copyToLibs(type: Copy) {
from configurations.testRuntimeClasspath
into "$buildDir/libs"
}
// Make sure test jar is copied
copyToLibs.dependsOn('testJar')
// Make sure platform-specific shell scripts are created after copyToLibs
startScripts.dependsOn(copyToLibs)
// Configure what goes into the tar / zip distribution file created by gradle distribution plugin assembleDist task
distributions {
main {
contents {
// Include test jar
from(testJar) {
into "lib"
}
// Include all dependencies from testRuntimeClasspath
from(copyToLibs) {
into "lib"
}
}
}
}
startScripts {
// Ensure ethat all testRuntimeClasspath dependencies are in classpath used by shell scripts
classpath = project.tasks['testJar'].outputs.files + project.configurations.testRuntimeClasspath
}
publishing {
repositories {
maven {
def releasesRepoUrl = "https://nexus.yourcompany.com/repository/maven-releases/"
def snapshotsRepoUrl = "https://nexus.yourcompany.com/repository/maven-snapshots/"
url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
credentials {
username = rootProject.getProperty('NEXUS_USERNAME')
password = rootProject.getProperty('NEXUS_PASSWORD')
}
}
}
publications {
maven(MavenPublication) {
groupId = 'com.yourgroupId'
version = "${rootProject.getVersion()}"
}
TestJar(MavenPublication) {
artifact(testJar)
}
RunTestsCliTar(MavenPublication) {
artifact(distTar)
artifactId "${applicationName}"
}
}
}
Now you can do the following:
To build the project (including the tar file) without running test task: gradle -x test clean build
To publish artifacts produced by project (including tar file to maven repo - in my case nexus): gradlew -x test publish. Note you will need to provide credentials to upload artifacts to repo. It is good practice to define them (NEXUS_USERNAME, NEXUS_PASSWORD in my example) in ~/.gradle/gradle.properties or specify them via -P options on the gradle command line.

Running Groovy script from Gradle using GroovyShell: Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/cli/ParseException

I want to run a groovy command-line script from my Gradle build script.
I'm using this code in my Gradle script:
def groovyShell = new GroovyShell();
groovyShell.run(file('script.groovy'), ['arg1', 'arg2'] as String[])
Things work fine until my Groovy script (script.groovy) uses the CliBuilder class. Then I get the following exception:
org.codehaus.groovy.runtime.InvokerInvocationException: java.lang.NoClassDefFoundError: org/apache/commons/cli/ParseException
...
Caused by: java.lang.ClassNotFoundException: org.apache.commons.cli.ParseException
I found lots of people with similar problems and errors, but "the solution" was difficult to extract from the numerous posts I read. Lots of people suggested putting the commons-cli jar on the classpath, but doing so for the GroovyShell was not at all apparent to me. Also, I had already declared #Grapes and #Grab for my required libraries in the script.groovy, so it should have everything it needed.
Thanks to this unaccepted SO answer, I finally found what I needed to do:
//define our own configuration
configurations{
addToClassLoader
}
//List the dependencies that our shell scripts will require in their classLoader:
dependencies {
addToClassLoader group: 'commons-cli', name: 'commons-cli', version: '1.2'
}
//Now add those dependencies to the root classLoader:
URLClassLoader loader = GroovyObject.class.classLoader
configurations.addToClassLoader.each {File file ->
loader.addURL(file.toURL())
}
//And now no more exception when I run this:
def groovyShell = new GroovyShell();
groovyShell.run(file('script.groovy'), ['arg1', 'arg2'] as String[])
You can find more details about classLoaders and why this solution works in this forum post.
Happy scripting!
(Before you downvote me for answering my own question, read this)
The alternative to do this is the following:
buildScript {
repositories { mavenCentral() }
dependencies {
classpath "commons-cli:commons-cli:1.2"
}
}
def groovyShell = new GroovyShell()
....
This puts the commons-cli dependency on the classpath of the buildscript instead of on the classpath of the project to be built.

How can I create a pathing jar in Gradle

When running groovyc in a Windows env, I am running into issues due to the length of the classpath, in my situation. I would like to work around this by creating a pathing jar, and then put that jar on the cp. How can I create a pathing jar w/ all of the classpath entries specified automatically in gradle and then add that jar to the cp?
Here is a tested solution:
task pathingJar(type: Jar) {
appendix = "pathing"
doFirst {
manifest {
attributes "Class-Path": configurations.compile.files.join(" ")
}
}
}
compileGroovy {
dependsOn(pathingJar)
classpath = files(pathingJar.archivePath)
}
Depending on your exact requirements, you might have to tweak this a bit. For example, if you have tests written in Groovy, you will also need a pathing Jar for the test compile class path. In this case you'll need to repeat above configuration as follows:
task testPathingJar(type: Jar) {
appendix = "testPathing"
doFirst {
manifest {
attributes "Class-Path": configurations.testCompile.files.join(" ")
}
}
}
compileTestGroovy {
dependsOn(testPathingJar)
classpath = files(testPathingJar.archivePath)
}
I finally got the "pathing jar" idea to work. I consider this to be a permanent workaround. This could be considered a solution if it is made part of gradle itself.
The original pathing jar code was provided by Peter, but it didn't work. The problem: classpath elements referenced in the pathing jar must be relative to the location of the pathing jar. So, this appears to work for me.
task pathingJar(type: Jar , dependsOn: 'cleanPathingJar') {
/**
* If the gradle_user_home env var has been set to
* C:\ on a Win7 machine, we may not have permission to write the jar to
* this directory, so we will write it to the caches subdir instead.
* This assumes a caches subdir containing the jars
* will always exist.
*/
gradleUserHome = new File(gradle.getGradleUserHomeDir(), "caches")
relativeClasspathEntries = configurations.compile.files.collect {
new File(gradleUserHome.getAbsolutePath()).toURI().
relativize(new File(it.getAbsolutePath()).toURI()).getPath()
}
appendix = "pathing"
destinationDir = gradleUserHome
doFirst {
manifest {
attributes "Class-Path": relativeClasspathEntries.join(" ")
}
}
}
compileGroovy {
dependsOn(pathingJar)
classpath = files(pathingJar.archivePath)
}
This is what helped me:
"The filename or extension is too long error" using gradle
In other words: use the com.github.ManifestClasspath plugin.
The other solutions did not work for me because the actual project main class ended up no being included in the classpath at execution time.

Resources