[
https://issues.apache.org/jira/browse/GROOVY-11871?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18065049#comment-18065049
]
ASF GitHub Bot commented on GROOVY-11871:
-----------------------------------------
Copilot commented on code in PR #2140:
URL: https://github.com/apache/groovy/pull/2140#discussion_r2917767202
##########
src/main/groovy/groovy/grape/GrapeUtil.groovy:
##########
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package groovy.grape
+
+import groovy.transform.CompileStatic
+import org.apache.groovy.plugin.GroovyRunner
+import org.apache.groovy.plugin.GroovyRunnerRegistry
+import org.codehaus.groovy.reflection.CachedClass
+import org.codehaus.groovy.reflection.ClassInfo
+import org.codehaus.groovy.runtime.m12n.ExtensionModuleScanner
+import org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl
+
+import java.util.jar.JarFile
+import java.util.zip.ZipEntry
+import java.util.zip.ZipException
+import java.util.zip.ZipFile
+
+/**
+ * Utility methods shared between GrapeIvy and GrapeMaven implementations.
+ */
+@CompileStatic
+class GrapeUtil {
+
+ private static final String METAINF_PREFIX = 'META-INF/services/'
+ private static final String RUNNER_PROVIDER_CONFIG = GroovyRunner.name
+
+ /**
+ * Adds a URI to a classloader's classpath via reflection.
+ */
+ static void addURL(ClassLoader loader, URI uri) {
+ // Dynamic invocation needed as addURL is not part of ClassLoader
interface
+ loader.metaClass.invokeMethod(loader, 'addURL', uri.toURL())
+ }
+
+ /**
+ * Processes and registers category methods (extension modules) from a JAR
file.
+ *
+ * @param loader the classloader to register methods with
+ * @param file the JAR file to process
+ */
+ static void processCategoryMethods(ClassLoader loader, File file) {
+ // register extension methods if jar
+ if (file.getName().toLowerCase().endsWith('.jar')) {
+ def mcRegistry = GroovySystem.metaClassRegistry
+ if (mcRegistry instanceof MetaClassRegistryImpl) {
+ try (JarFile jar = new JarFile(file)) {
+ ZipEntry entry =
jar.getEntry(ExtensionModuleScanner.MODULE_META_INF_FILE)
+ if (!entry) {
+ entry =
jar.getEntry(ExtensionModuleScanner.LEGACY_MODULE_META_INF_FILE)
+ }
+ if (entry) {
+ Properties props = new Properties()
+
+ try (InputStream is = jar.getInputStream(entry)) {
+ props.load(is)
+ }
+
+ Map<CachedClass, List<MetaMethod>> metaMethods = [:]
+
mcRegistry.registerExtensionModuleFromProperties(props, loader, metaMethods)
+ // add old methods to the map
+ metaMethods.each { CachedClass c, List<MetaMethod>
methods ->
+ // GROOVY-5543: if a module was loaded using grab,
there are chances that subclasses
+ // have their own ClassInfo, and we must change
them as well!
+ Set<CachedClass> classesToBeUpdated = [c].toSet()
+ ClassInfo.onAllClassInfo { ClassInfo info ->
+ if
(c.getTheClass().isAssignableFrom(info.getCachedClass().getTheClass())) {
+ classesToBeUpdated << info.getCachedClass()
+ }
+ }
+ classesToBeUpdated*.addNewMopMethods(methods)
+ }
+ }
+ } catch (ZipException e) {
+ throw new RuntimeException("Grape could not load jar
'$file'", e)
+ }
+ }
+ }
+ }
+
+ /**
+ * Searches the given File for known service provider configuration files
to process.
+ *
+ * @param loader used to locate service provider files
+ * @param f ZipFile in which to search for services
+ * @return a collection of service provider files that were found
+ */
+ static Collection<String> processMetaInfServices(ClassLoader loader, File
f) {
+ List<String> services = []
+ try (ZipFile zf = new ZipFile(f)) {
+ String providerConfig =
'org.codehaus.groovy.runtime.SerializedCategoryMethods'
+ ZipEntry serializedCategoryMethods = zf.getEntry(METAINF_PREFIX +
providerConfig)
+ if (serializedCategoryMethods != null) {
+ services.add(providerConfig)
+
+ try (InputStream is =
zf.getInputStream(serializedCategoryMethods)) {
+ processSerializedCategoryMethods(is)
+ }
+ }
+ // TODO: remove in a future release (replaced by
GroovyRunnerRegistry)
+ providerConfig = 'org.codehaus.groovy.plugins.Runners'
+ ZipEntry pluginRunners = zf.getEntry(METAINF_PREFIX +
providerConfig)
+ if (pluginRunners != null) {
+ services.add(providerConfig)
+
+ try (InputStream is = zf.getInputStream(pluginRunners)) {
+ processRunners(is, f.getName(), loader)
+ }
+ }
+ // GroovyRunners are loaded per ClassLoader using a ServiceLoader
so here
+ // it only needs to be indicated that the service provider file
was found
+ if (zf.getEntry(METAINF_PREFIX + RUNNER_PROVIDER_CONFIG) != null) {
+ services.add(RUNNER_PROVIDER_CONFIG)
+ }
+ } catch (ZipException ignore) {
+ // ignore files we can't process, e.g. non-jar/zip artifacts
+ // TODO: log a warning
+ }
+ services
+ }
+
+ /**
+ * Processes serialized category methods from an input stream.
+ *
+ * @param is the input stream containing category method definitions
+ */
+ static void processSerializedCategoryMethods(InputStream is) {
+ is.getText().readLines().each {
+ System.err.println(it.trim()) // TODO: implement this or delete it
+ }
+ }
+
+ /**
+ * Processes and registers Groovy runner implementations from a service
provider file.
+ *
+ * @param is the input stream containing runner class names
+ * @param name the name to register the runners under
+ * @param loader the classloader to load runner classes from
+ */
+ static void processRunners(InputStream is, String name, ClassLoader
loader) {
+ GroovyRunnerRegistry registry = GroovyRunnerRegistry.instance
+ is.getText().readLines()*.trim().each { String line ->
+ if (!line.isEmpty() && line[0] != '#')
+ try {
+ registry[name] = (GroovyRunner)
loader.loadClass(line).getDeclaredConstructor().newInstance()
+ } catch (Exception e) {
+ throw new IllegalStateException("Error registering runner
class '$line'", e)
Review Comment:
`processRunners` has an `if (!line.isEmpty() && line[0] != '#')` with no
braces/body, so the subsequent `try { ... }` runs for *every* line, including
empty/comment lines. This will attempt to load invalid class names and can
break runner discovery. Wrap the `try` block inside the `if` (or add `continue`
when the condition fails).
```suggestion
if (!line.isEmpty() && line[0] != '#') {
try {
registry[name] = (GroovyRunner)
loader.loadClass(line).getDeclaredConstructor().newInstance()
} catch (Exception e) {
throw new IllegalStateException("Error registering
runner class '$line'", e)
}
```
##########
subprojects/groovy-xml/src/main/groovy/groovy/xml/StaxBuilder.groovy:
##########
@@ -31,7 +31,10 @@ package groovy.xml
* elem2('hello2')
* elem3(x:7)
* }
- * assert writer.toString() == '<?xml version="1.0" ?><root1 a="5"
b="7"><elem1>hello1</elem1><elem2>hello2</elem2><elem3 x="7"></elem3></root1>'
+ * def pretty= writer.toString()
+ * .replaceAll(/<\?xml[^>]*>/, '') // remove XML declaration
Review Comment:
The doc example removes the XML declaration using
`.replaceAll(/<\?xml[^>]*>/, '')` but (as shown in the old asserted output)
implementations may emit a trailing space after the declaration. That leaves a
leading space before `<root1...>` and makes the sample assertion brittle.
Consider stripping the declaration plus any following whitespace (or trimming)
in the example as well.
```suggestion
* .replaceAll(/<\?xml[^>]*>\s*/, '') // remove XML declaration and any
following whitespace
```
##########
subprojects/groovy-console/src/main/groovy/groovy/console/ui/Console.groovy:
##########
@@ -1242,12 +1249,7 @@ class Console implements CaretListener,
HyperlinkListener, ComponentListener, Fo
// actually run the script
void runScript(EventObject evt = null, SourceType st = new
GroovySourceType()) {
Review Comment:
`runScript` no longer calls `saveInputAreaContentHash()`.
`runSelectedScript` still does, so this looks like an unintended regression
from the previous implementation, and may break dirty-state tracking or other
logic relying on the hash being updated whenever a script is run. Keep the
ternary if desired, but preserve the `saveInputAreaContentHash()` call at the
start of `runScript`.
```suggestion
void runScript(EventObject evt = null, SourceType st = new
GroovySourceType()) {
saveInputAreaContentHash()
```
##########
subprojects/groovy-xml/src/spec/test/StaxBuilderTest.groovy:
##########
@@ -32,7 +32,9 @@ class StaxBuilderTest extends GroovyTestCase {
elem2('world')
}
- assert writer.toString() == '<?xml version="1.0" ?><root
attribute="1"><elem1>hello</elem1><elem2>world</elem2></root>'
+ def pretty= writer.toString()
+ .replaceAll(/<\?xml[^>]*>/, '') // remove XML declaration
Review Comment:
`writer.toString()` previously included a space after the XML declaration
(`<?xml ... ?> <root...>`). After `.replaceAll(/<\?xml[^>]*>/, '')`, that
leading space will remain, so `pretty` is likely `' <root...` and this
assertion will fail depending on the StAX writer implementation. Consider
removing the declaration *and* any adjacent whitespace (e.g., include `\s*` in
the regex or trim the result) before comparing.
```suggestion
.replaceAll(/<\?xml[^>]*>\s*/, '') // remove XML declaration and
any following whitespace
```
##########
subprojects/groovy-grape-maven/src/test/groovy/groovy/grape/maven/GrapeMavenTest.groovy:
##########
@@ -0,0 +1,165 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package groovy.grape.maven
+
+import groovy.grape.Grape
+import org.junit.jupiter.api.AfterAll
+import org.junit.jupiter.api.BeforeAll
+import org.junit.jupiter.api.Test
+
+import java.nio.file.Files
+import java.util.jar.JarOutputStream
+
+final class GrapeMavenTest {
+ private static File writeEmptyJar(File jarFile) {
+ jarFile.parentFile.mkdirs()
+ jarFile.withOutputStream { os ->
+ new JarOutputStream(os).close()
+ }
+ jarFile
+ }
+
+ private static void deleteCachedGroup(String groupId) {
+ File cachedGroupDir = new File(GrapeMaven.grapeCacheDir,
groupId.replace('.' as char, File.separatorChar))
+ if (cachedGroupDir.exists()) {
+ cachedGroupDir.deleteDir()
+ }
+ }
+
+ private static File writePom(File pomFile, String groupId, String
artifactId, String version, List<Map<String, Object>> deps = []) {
+ pomFile.parentFile.mkdirs()
+ String depsXml = deps.collect { Map<String, Object> dep ->
+ String optionalXml = dep.optional ? '\n
<optional>true</optional>' : ''
+ """\
+ <dependency>
+ <groupId>${dep.groupId}</groupId>
+ <artifactId>${dep.artifactId}</artifactId>
+ <version>${dep.version}</version>
+ <scope>compile</scope>${optionalXml}
+ </dependency>""".stripIndent()
+ }.join('\n')
+
+ pomFile.text = (
+"""<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>${groupId}</groupId>
+ <artifactId>${artifactId}</artifactId>
+ <version>${version}</version>
+ <packaging>jar</packaging>
+ <dependencies>
+${depsXml}
+ </dependencies>
+</project>
+"""
+ )
+ pomFile
+ }
+
+ private static void publishArtifact(File repoDir, String groupId, String
artifactId, String version, List<Map<String, Object>> deps = []) {
+ String relPath = groupId.replace('.', '/') +
"/${artifactId}/${version}"
+ File artifactDir = new File(repoDir, relPath)
+ writePom(new File(artifactDir, "${artifactId}-${version}.pom"),
groupId, artifactId, version, deps)
+ writeEmptyJar(new File(artifactDir, "${artifactId}-${version}.jar"))
+ }
+
+ private static Set<String> jarNames(GroovyClassLoader loader) {
+ loader.URLs.collect { url -> url.path.split('/')[-1] } as Set
+ }
+
+ @BeforeAll
+ static void setUpClass() {
+ System.setProperty('groovy.grape.impl',
'groovy.grape.maven.GrapeMaven')
+ Grape.reset()
+ }
+
+ @AfterAll
+ static void cleanup() {
+ Grape.reset()
+ }
Review Comment:
This test sets the global `groovy.grape.impl` system property in
`@BeforeAll` but `@AfterAll` only calls `Grape.reset()` and never
restores/clears the property. That can leak into subsequent tests and change
which engine they run against. Capture the previous property value in
`setUpClass()` and restore it (or clear it) in `cleanup()`.
##########
subprojects/groovy-grape-maven/src/main/groovy/groovy/grape/maven/GrapeMaven.groovy:
##########
@@ -0,0 +1,747 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package groovy.grape.maven
+
+import groovy.grape.GrapeEngine
+import groovy.grape.GrapeUtil
+import groovy.transform.AutoFinal
+import groovy.transform.CompileDynamic
+import groovy.transform.CompileStatic
+import groovy.transform.NamedParam
+import groovy.transform.NamedParams
+import org.codehaus.groovy.reflection.ReflectionUtils
+import org.eclipse.aether.AbstractRepositoryListener
+import org.eclipse.aether.RepositoryEvent
+import org.eclipse.aether.RepositorySystem
+import org.eclipse.aether.RepositorySystemSession
+import org.eclipse.aether.artifact.Artifact
+import org.eclipse.aether.artifact.DefaultArtifact
+import org.eclipse.aether.collection.CollectRequest
+import org.eclipse.aether.graph.Dependency
+import org.eclipse.aether.graph.DependencyFilter
+import org.eclipse.aether.graph.DependencyNode
+import org.eclipse.aether.internal.impl.scope.OptionalDependencySelector
+import org.eclipse.aether.internal.impl.scope.ScopeDependencySelector
+import org.eclipse.aether.repository.LocalRepository
+import org.eclipse.aether.repository.RemoteRepository
+import org.eclipse.aether.repository.RepositoryPolicy
+import org.eclipse.aether.resolution.ArtifactRequest
+import org.eclipse.aether.resolution.ArtifactResult
+import org.eclipse.aether.resolution.DependencyRequest
+import org.eclipse.aether.resolution.DependencyResolutionException
+import org.eclipse.aether.supplier.RepositorySystemSupplier
+import org.eclipse.aether.transfer.AbstractTransferListener
+import org.eclipse.aether.transfer.TransferEvent
+import org.eclipse.aether.util.graph.selector.AndDependencySelector
+import org.eclipse.aether.util.repository.SimpleArtifactDescriptorPolicy
+
+import java.util.concurrent.CopyOnWriteArrayList
+
+/**
+ * Implementation supporting {@code @Grape} and {@code @Grab} annotations
based on Maven.
+ */
+@AutoFinal
+@CompileStatic
+class GrapeMaven implements GrapeEngine {
+ private static final List<String> DEFAULT_CONF =
Collections.singletonList('default')
+ private static final Map<String, Set<String>> MUTUALLY_EXCLUSIVE_KEYS =
processGrabArgs([
+ ['group', 'groupId', 'organisation', 'organization', 'org'],
+ ['module', 'artifactId', 'artifact'],
+ ['version', 'revision', 'rev'],
+ ['conf', 'scope', 'configuration'],
+ ])
+ private static final boolean DEBUG_GRAB =
Boolean.getBoolean('groovy.grape.debug')
+
+ @CompileDynamic // maps a->[b,c], b->[a,c] and c->[a,b] given [a,b,c]
+ private static Map<String, Set<String>> processGrabArgs(List<List<String>>
grabArgs) {
+ grabArgs.inject([:]) { Map m, List g -> g.each { a -> m[a] = (g - a)
as Set }; m }
+ }
+
+ // weak hash map so we don't leak loaders directly
+ final Map<ClassLoader, Set<MavenGrabRecord>> loadedDeps = [] as WeakHashMap
+ /** Stores the MavenGrabRecord(s) for all dependencies in each grab()
call. */
+ final Set<MavenGrabRecord> grabRecordsForCurrDependencies = [] as
LinkedHashSet
+ boolean enableGrapes = true
+ final List<Closure> progressListeners = new CopyOnWriteArrayList<>()
+ final List<RemoteRepository> repos = [
+ new RemoteRepository.Builder("central", "default",
"https://repo.maven.apache.org/maven2/").build()
+ ]
+
+ @CompileDynamic
+ void addProgressListener(Closure listener) {
+ if (listener != null) {
+ progressListeners.add(listener)
+ }
+ }
+
+ void removeProgressListener(Closure listener) {
+ progressListeners.remove(listener)
+ }
+
+ private boolean hasProgressListeners() {
+ !progressListeners.isEmpty()
+ }
+
+ @CompileDynamic
+ private void fireProgressEvent(String type, String name) {
+ if (!name) return
+ Map<String, String> event = [type: type, name: name]
+ progressListeners.each { Closure listener ->
+ listener.call(event)
+ }
+ }
+
+ private AbstractRepositoryListener createRepositoryListener() {
+ new AbstractRepositoryListener() {
+ @Override
+ void artifactResolving(RepositoryEvent event) {
+ Artifact artifact = event?.artifact
+ if (artifact != null && artifact.extension != 'pom') {
+ fireProgressEvent('resolving', artifact.toString())
+ }
+ }
+ }
+ }
+
+ private AbstractTransferListener createTransferListener() {
+ new AbstractTransferListener() {
+ @Override
+ void transferInitiated(TransferEvent event) {
+ String resourceName = event?.resource?.resourceName
+ String displayName = displayDownloadName(resourceName)
+ if (displayName != null) {
+ fireProgressEvent('downloading', displayName)
+ }
+ }
+ }
+ }
+
+ private static String displayDownloadName(String resourceName) {
+ if (!resourceName) return null
+ String fileName = resourceName.tokenize('/').last()
+ if (!fileName
+ || fileName.startsWith('maven-metadata')
+ || fileName.endsWith('.pom')
+ || fileName.endsWith('.sha1')
+ || fileName.endsWith('.md5')
+ || fileName.endsWith('.asc')) {
+ return null
+ }
+ fileName
+ }
+
+ /**
+ * Grab the endorsed module for the current Groovy version.
+ */
+ @Override
+ grab(String endorsedModule) {
+ grab(group: 'groovy.endorsed', module: endorsedModule, version:
GroovySystem.getVersion())
+ }
+
+ @Override
+ grab(Map args) {
+ args.calleeDepth = args.calleeDepth ?: DEFAULT_CALLEE_DEPTH + 1
+ grab(args, args)
+ }
+
+ @Override
+ grab(Map args, Map... dependencies) {
+ ClassLoader loader = null
+ grabRecordsForCurrDependencies.clear()
+
+ try {
+ // identify the target classloader early, so we fail before
checking repositories
+ loader = chooseClassLoader(
+ refObject: args.remove('refObject'),
+ classLoader: args.remove('classLoader'),
+ calleeDepth: args.calleeDepth ?: DEFAULT_CALLEE_DEPTH,
+ )
+
+ // check for non-fail null
+ // if we were in fail mode we would have already thrown an
exception
+ if (!loader) return
+
+ URI[] uris = resolve(loader, args, dependencies)
+ for (URI uri : uris) {
+ GrapeUtil.addURL(loader, uri)
+ }
+ boolean runnerServicesFound = false
+ for (URI uri : uris) {
+ // TODO: check artifact type, jar vs library, etc.
+ File file = new File(uri)
+ GrapeUtil.processCategoryMethods(loader, file)
+ Collection<String> services =
GrapeUtil.processMetaInfServices(loader, file)
+ if (!runnerServicesFound) {
+ runnerServicesFound = GrapeUtil.checkForRunner(services)
+ }
+ }
+ if (runnerServicesFound) {
+ GrapeUtil.registryLoad(loader)
+ }
+ } catch (Exception e) {
+ // clean-up the state first
+ Set<MavenGrabRecord> grabRecordsForCurrLoader =
getLoadedDepsForLoader(loader)
+ grabRecordsForCurrLoader.removeAll(grabRecordsForCurrDependencies)
+ grabRecordsForCurrDependencies.clear()
+
+ if (args.noExceptions) {
+ return e
+ }
+ throw asRuntimeGrabError(e)
+ }
+ null
+ }
+
+
+ @Override
+ @CompileDynamic
+ Map<String, Map<String, List<String>>> enumerateGrapes() {
+ Map<String, Map<String, List<String>>> bunches = [:]
+ File cacheRoot = grapeCacheDir.canonicalFile
+
+ cacheRoot.eachFileRecurse { File f ->
+ if (!f.isFile()) return
+ String name = f.name
+ if (name.endsWith('.sha1') || name.endsWith('.md5') ||
name.endsWith('.asc')) return
+
+ File versionDir = f.parentFile
+ File moduleDir = versionDir?.parentFile
+ File groupDir = moduleDir?.parentFile
+ if (!versionDir || !moduleDir || !groupDir) return
+
+ String version = versionDir.name
+ String module = moduleDir.name
+ String expectedPrefix = module + '-' + version
+ if (!name.startsWith(expectedPrefix)) return
+
+ String groupPath =
cacheRoot.toPath().relativize(groupDir.toPath()).toString()
+ if (!groupPath) return
+ String groupKey = groupPath.replace(File.separatorChar, '.' as
char)
+
+ Map<String, List<String>> modules =
bunches.computeIfAbsent(groupKey) { [:] }
+ List<String> versions = modules.computeIfAbsent(module) { [] }
+ if (!versions.contains(version)) {
+ versions << version
+ }
+ }
+
+ bunches.values()*.values()*.each { List<String> versions ->
versions.sort() }
+ bunches
+ }
+
+ void uninstallArtifact(String group, String module, String rev) {
+ String groupPath = group.replace('.' as char, File.separatorChar)
+ def artifactDir = new File(grapeCacheDir, groupPath + File.separator +
module + File.separator + rev)
+ if (artifactDir.exists()) {
+ artifactDir.deleteDir()
+ }
+ }
+
+ @Override
+ URI[] resolve(Map args, Map... dependencies) {
+ resolve(args, null, dependencies)
+ }
+
+ @Override
+ URI[] resolve(Map args, List depsInfo, Map... dependencies) {
+ // identify the target classloader early, so we fail before checking
repositories
+ ClassLoader loader = chooseClassLoader(
+ refObject: args.remove('refObject'),
+ classLoader: args.remove('classLoader'),
+ calleeDepth: args.calleeDepth ?: DEFAULT_CALLEE_DEPTH,
+ )
+
+ // check for non-fail null
+ // if we were in fail mode we would have already thrown an exception
+ if (!loader) {
+ return new URI[0]
+ }
+
+ resolve(loader, args, depsInfo, dependencies)
+ }
+
+ private Set<MavenGrabRecord> getLoadedDepsForLoader(ClassLoader loader) {
+ // use a LinkedHashSet to preserve the initial insertion order
+ loadedDeps.computeIfAbsent(loader, k -> [] as LinkedHashSet)
+ }
+
+ URI[] resolve(ClassLoader loader, Map args, Map... dependencies) {
+ resolve(loader, args, null, dependencies)
+ }
+
+ URI[] resolve(ClassLoader loader, Map args, List depsInfo, Map...
dependencies) {
+ if (!enableGrapes) {
+ return new URI[0]
+ }
+
+ boolean populateDepsInfo = (depsInfo != null)
+ Set<MavenGrabRecord> localDeps = getLoadedDepsForLoader(loader)
+ List<MavenGrabRecord> grabRecords = []
+ for (Map dep : dependencies) {
+ MavenGrabRecord mgr = createGrabRecord(dep)
+ grabRecordsForCurrDependencies.add(mgr)
+ localDeps.add(mgr)
+ grabRecords.add(mgr)
+ }
+
+ try {
+ URI[] results = getDependencies(args, populateDepsInfo ? depsInfo
: null, grabRecords as MavenGrabRecord[])
+ return results
+ } catch (Exception e) {
+ localDeps.removeAll(grabRecordsForCurrDependencies)
+ grabRecordsForCurrDependencies.clear()
+ throw asRuntimeGrabError(e)
+ }
+ }
+
+ URI[] getDependencies(Map args, List depsInfo, MavenGrabRecord...
grabRecords) {
+ try (RepositorySystem system = new RepositorySystemSupplier().get()) {
+ def localRepo = new LocalRepository(grapeCacheDir.toPath())
+ String checksumPolicy = args.disableChecksums ?
Review Comment:
`try (RepositorySystem system = new RepositorySystemSupplier().get())` uses
try-with-resources on `org.eclipse.aether.RepositorySystem`, which (in Maven
Resolver/Aether) is not an `AutoCloseable` resource. Under `@CompileStatic`
this is very likely to fail compilation. Use a plain local variable for the
repository system (and keep the session as the closable resource), or otherwise
manage the actual closeable object returned by the supplier if one exists.
> Support Maven Resolver based version of Grapes
> ----------------------------------------------
>
> Key: GROOVY-11871
> URL: https://issues.apache.org/jira/browse/GROOVY-11871
> Project: Groovy
> Issue Type: Improvement
> Reporter: Paul King
> Priority: Major
>
--
This message was sent by Atlassian Jira
(v8.20.10#820010)