Skip to content

Commit 087e138

Browse files
committed
refactor: for a cleaner runtime classpath, keep logging related tests in a separate subproject
1 parent 2e3f030 commit 087e138

21 files changed

Lines changed: 368 additions & 183 deletions

File tree

.github/workflows/groovy-build-test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ jobs:
5454
strategy:
5555
fail-fast: false
5656
matrix:
57-
java: [18, 19, 20, 22, 23, 24]
57+
java: [18, 19, 20, 22, 23, 24, 26]
5858
runs-on: ubuntu-latest
5959
steps:
6060
- uses: actions/checkout@v6

build-logic/src/main/groovy/org.apache.groovy-tested.gradle

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
*/
1919

2020
import org.apache.groovy.gradle.ConcurrentExecutionControlBuildService
21+
import org.apache.groovy.gradle.TargetJavaHomeSupport
2122

2223
// TODO: Instead of adding to the test sources, these should be a
2324
// separate source set so that we can run spec tests in isolation
@@ -44,15 +45,9 @@ dependencies {
4445
tasks.withType(Test).configureEach {
4546
def fs = objects.newInstance(TestServices).fileSystemOperations
4647
def grapeDirectory = new File(temporaryDir, 'grape')
47-
def jdk8 = ['-XX:+UseConcMarkSweepGC']
48-
def jdk9 = ['-Djava.locale.providers=COMPAT,SPI']
49-
def common = ['-ea', "-Xms${groovyJUnit_ms}", "-Xmx${groovyJUnit_mx}", '-Duser.language=en']
50-
if (JavaVersion.current().isJava9Compatible()) {
51-
jvmArgs(*common, *jdk9)
52-
systemProperty 'groovy.force.illegal.access', findProperty('groovy.force.illegal.access')
53-
} else {
54-
jvmArgs(*common, *jdk8)
55-
}
48+
def options = ['-ea', "-Xms${groovyJUnit_ms}", "-Xmx${groovyJUnit_mx}", '-Duser.language=en', '-Djava.locale.providers=COMPAT,SPI']
49+
jvmArgs(*options)
50+
systemProperty 'groovy.force.illegal.access', findProperty('groovy.force.illegal.access')
5651
def testdb = System.properties['groovy.testdb.props']
5752
if (testdb) {
5853
systemProperty 'groovy.testdb.props', testdb
@@ -70,12 +65,10 @@ tasks.withType(Test).configureEach {
7065
userHome: temporaryDir // make sure tests are isolated from real user home or tests using Grape may fail
7166
))
7267

73-
if (rootProject.hasProperty('target.java.home')) {
74-
String targetJavaHome = rootProject.property('target.java.home')?.trim()
75-
if (targetJavaHome) {
76-
executable = "${targetJavaHome}/bin/java"
77-
println "Using ${executable} to run tests"
78-
}
68+
String targetJavaHome = TargetJavaHomeSupport.targetJavaHome(project)
69+
if (targetJavaHome) {
70+
executable = TargetJavaHomeSupport.javaExecutable(targetJavaHome)
71+
println "Using ${executable} to run tests"
7972
}
8073

8174
forkEvery = 50
@@ -109,6 +102,17 @@ tasks.withType(Test).configureEach {
109102
}
110103
}
111104

105+
tasks.withType(GroovyCompile).configureEach {
106+
if (name == 'compileTestGroovy') {
107+
Integer feature = TargetJavaHomeSupport.featureVersionFromReleaseFile(TargetJavaHomeSupport.targetJavaHome(project))
108+
if (feature != null) {
109+
javaLauncher = javaToolchains.launcherFor {
110+
languageVersion = JavaLanguageVersion.of(feature)
111+
}
112+
}
113+
}
114+
}
115+
112116
class TestCommandLineArgumentProvider implements CommandLineArgumentProvider {
113117

114118
@Internal
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.groovy.gradle
20+
21+
import groovy.transform.CompileStatic
22+
import org.gradle.api.Project
23+
24+
@CompileStatic
25+
final class TargetJavaHomeSupport {
26+
27+
private TargetJavaHomeSupport() {}
28+
29+
/** Returns the trimmed value of the {@code target.java.home} Gradle property, or {@code null} if absent/blank. */
30+
static String targetJavaHome(Project project) {
31+
if (!project.rootProject.hasProperty('target.java.home')) return null
32+
String value = project.rootProject.property('target.java.home')?.toString()?.trim()
33+
return value ?: null
34+
}
35+
36+
/** Returns the {@code java} executable path for the given Java home directory. */
37+
static String javaExecutable(String javaHome) {
38+
return javaHome ? "${javaHome}/bin/java" : null
39+
}
40+
41+
/**
42+
* Reads the {@code release} file inside the given Java home and returns the
43+
* feature release number (e.g. 17 for "17.0.2", 21 for "21"), or {@code null}
44+
* if the file is absent or unparseable.
45+
*/
46+
static Integer featureVersionFromReleaseFile(String javaHome) {
47+
if (!javaHome) return null
48+
File releaseFile = new File(javaHome, 'release')
49+
if (!releaseFile.exists()) return null
50+
String line = releaseFile.readLines().find { it.startsWith('JAVA_VERSION=') }
51+
if (!line) return null
52+
def matcher = (line =~ /\"(\d+)(?:\.\d+)*\"/)
53+
return matcher.find() ? (matcher.group(1) as Integer) : null
54+
}
55+
}
56+

build.gradle

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,6 @@ dependencies {
115115
testImplementation projects.groovyMacro
116116
testImplementation projects.groovyDateutil
117117
testImplementation "net.jcip:jcip-annotations:${versions.jcipAnnotations}"
118-
testImplementation "ch.qos.logback:logback-classic:${versions.logback}"
119-
testImplementation "log4j:log4j:${versions.log4j}"
120-
testImplementation "org.apache.logging.log4j:log4j-core:${versions.log4j2}"
121-
testImplementation "org.slf4j:jcl-over-slf4j:${versions.slf4j}"
122118
testImplementation "com.thoughtworks.qdox:qdox:${versions.qdox}"
123119
testImplementation "com.fasterxml.jackson.core:jackson-databind:${versions.jackson}"
124120
testImplementation "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:${versions.jackson}"

settings.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ def subprojects = [
6161
'groovy-jmx',
6262
'groovy-json',
6363
'groovy-jsr223',
64+
'groovy-logging-test',
6465
'groovy-ginq',
6566
'groovy-macro',
6667
'groovy-macro-library',

src/spec/doc/core-metaprogramming.adoc

Lines changed: 1 addition & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -2115,159 +2115,7 @@ In this example, we also set the `strict` parameter to false, which allows us to
21152115
21162116
Deprecated. Consider using traits instead.
21172117
2118-
==== Logging improvements
2119-
2120-
Groovy provides a family of AST transformations that help with integration of the most widely
2121-
used logging frameworks. There is a transform and associated annotation for each of the common frameworks.
2122-
These transforms provide a streamlined declarative approach to using the logging framework.
2123-
In each case, the transform will:
2124-
2125-
* add a static final `log` field to the annotated class corresponding to the logger
2126-
* wrap all calls to `log.level()` into the appropriate `log.isLevelEnabled` guard, depending on the underlying framework
2127-
2128-
Those transformations support two parameters:
2129-
2130-
* `value` (default `log`) corresponds to the name of the logger field
2131-
* `category` (defaults to the class name) is the name of the logger category
2132-
2133-
It's worth noting that annotating a class with one of those annotations doesn't
2134-
prevent you from using the logging framework using the normal long-hand approach.
2135-
2136-
[[xform-Log]]
2137-
===== `@groovy.util.logging.Log`
2138-
2139-
The first logging AST transformation available is the `@Log` annotation which relies on the JDK logging framework. Writing:
2140-
2141-
[source,groovy]
2142-
----
2143-
include::../test/LogImprovementsASTTransformsTest.groovy[tags=log_spec,indent=0]
2144-
----
2145-
2146-
is equivalent to writing:
2147-
2148-
[source,groovy]
2149-
----
2150-
include::../test/LogImprovementsASTTransformsTest.groovy[tags=log_equiv,indent=0]
2151-
----
2152-
2153-
[[xform-Commons]]
2154-
===== `@groovy.util.logging.Commons`
2155-
2156-
Groovy supports the http://commons.apache.org/proper/commons-logging/[Apache Commons Logging] framework using the
2157-
`@Commons` annotation. Writing:
2158-
2159-
[source,groovy]
2160-
----
2161-
include::../test/LogImprovementsASTTransformsTest.groovy[tags=commons_spec,indent=0]
2162-
----
2163-
2164-
is equivalent to writing:
2165-
2166-
[source,groovy]
2167-
----
2168-
include::../test/LogImprovementsASTTransformsTest.groovy[tags=commons_equiv,indent=0]
2169-
----
2170-
2171-
You still need to add the appropriate commons-logging jar to your classpath.
2172-
2173-
[[xform-Log4j]]
2174-
===== `@groovy.util.logging.Log4j`
2175-
2176-
Groovy supports the http://logging.apache.org/log4j/1.2/[Apache Log4j 1.x] framework using the
2177-
`@Log4j` annotation. Writing:
2178-
2179-
[source,groovy]
2180-
----
2181-
include::../test/LogImprovementsASTTransformsTest.groovy[tags=log4j_spec,indent=0]
2182-
----
2183-
2184-
is equivalent to writing:
2185-
2186-
[source,groovy]
2187-
----
2188-
include::../test/LogImprovementsASTTransformsTest.groovy[tags=log4j_equiv,indent=0]
2189-
----
2190-
2191-
You still need to add the appropriate log4j jar to your classpath.
2192-
This annotation can also be used with the compatible https://reload4j.qos.ch/[reload4j] log4j
2193-
drop-in replacement, just use the jar from that project instead of a log4j jar.
2194-
2195-
[[xform-Log4j2]]
2196-
===== `@groovy.util.logging.Log4j2`
2197-
2198-
Groovy supports the http://logging.apache.org/log4j/2.x/[Apache Log4j 2.x] framework using the
2199-
`@Log4j2` annotation. Writing:
2200-
2201-
[source,groovy]
2202-
----
2203-
include::../test/LogImprovementsASTTransformsTest.groovy[tags=log4j2_spec,indent=0]
2204-
----
2205-
2206-
is equivalent to writing:
2207-
2208-
[source,groovy]
2209-
----
2210-
include::../test/LogImprovementsASTTransformsTest.groovy[tags=log4j2_equiv,indent=0]
2211-
----
2212-
2213-
You still need to add the appropriate log4j2 jar to your classpath.
2214-
2215-
[[xform-Slf4j]]
2216-
===== `@groovy.util.logging.Slf4j`
2217-
2218-
Groovy supports the http://www.slf4j.org/[Simple Logging Facade for Java (SLF4J)] framework using the
2219-
`@Slf4j` annotation. Writing:
2220-
2221-
[source,groovy]
2222-
----
2223-
include::../test/LogImprovementsASTTransformsTest.groovy[tags=slf4j_spec,indent=0]
2224-
----
2225-
2226-
is equivalent to writing:
2227-
2228-
[source,groovy]
2229-
----
2230-
include::../test/LogImprovementsASTTransformsTest.groovy[tags=slf4j_equiv,indent=0]
2231-
----
2232-
2233-
You still need to add the appropriate slf4j jar(s) to your classpath.
2234-
2235-
[[xform-PlatformLog]]
2236-
===== `@groovy.util.logging.PlatformLog`
2237-
2238-
Groovy supports the https://openjdk.org/jeps/264[Java Platform Logging API and Service]
2239-
framework using the `@PlatformLog` annotation. Writing:
2240-
2241-
[source,groovy]
2242-
----
2243-
@groovy.util.logging.PlatformLog
2244-
class Greeter {
2245-
void greet() {
2246-
log.info 'Called greeter'
2247-
println 'Hello, world!'
2248-
}
2249-
}
2250-
----
2251-
2252-
is equivalent to writing:
2253-
2254-
[source,groovy]
2255-
----
2256-
import java.lang.System.Logger
2257-
import java.lang.System.LoggerFinder
2258-
import static java.lang.System.Logger.Level.INFO
2259-
2260-
class Greeter {
2261-
private static final transient Logger log =
2262-
LoggerFinder.loggerFinder.getLogger(Greeter.class.name, Greeter.class.module)
2263-
void greet() {
2264-
log.log INFO, 'Called greeter'
2265-
println 'Hello, world!'
2266-
}
2267-
}
2268-
----
2269-
2270-
You need to be using JDK 9+ to use this capability.
2118+
include::../../../subprojects/groovy-logging-test/src/spec/doc/logging-transforms.adoc[]
22712119
22722120
==== Declarative concurrency
22732121

src/test/groovy/org/codehaus/groovy/reflection/SecurityTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ public void setUp() {
102102
* at java.base/java.lang.invoke.BootstrapMethodInvoker.invoke(BootstrapMethodInvoker.java:127)
103103
*/
104104
VMPluginFactory.getPlugin();
105+
// ReflectionUtils initializes StackWalker, which can trigger reflective access checks.
106+
// Warm it up before the restrictive manager is active.
107+
ReflectionUtils.getCallingClass();
105108

106109
forbidden = new Permissions();
107110
forbidden.add(new ReflectPermission("suppressAccessChecks"));

0 commit comments

Comments
 (0)