You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Artifactory 5.5.1元数据写入时出现ConcurrentModificationException问题求助

ConcurrentModificationException When Publishing to Specific Artifactory Repository (Groovy Plugin Root Cause & Fix)

我之前碰到过一模一样的问题,这个ConcurrentModificationException的根源很明确:你在同步的afterCreate钩子中执行了会触发同类钩子的操作。具体来说,当你往stage-release仓库上传构件时,钩子会触发repositories.copy把构件复制到release仓库,而这个复制操作本身又会触发release仓库的afterCreate钩子——此时原钩子的执行还没完成,插件系统正在遍历钩子动作集合,新的钩子触发会修改这个集合,直接导致Guava的LinkedHashMultimap抛出并发修改异常。

解决方案1:用异步任务执行复制操作

把复制逻辑放到异步任务里,让当前的afterCreate钩子先执行完毕,再异步处理复制,这样就不会干扰正在遍历的钩子集合。修改后的代码如下:

import org.artifactory.exception.CancelException
import org.artifactory.repo.RepoPathFactory
import org.artifactory.api.context.ContextHelper

storage {
    afterCreate { item ->
        def repoKey = item.repoPath.repoKey
        if (repoKey == 'stage-release') {
            def sourcePath = RepoPathFactory.create(repoKey, item.repoPath.path)
            def destPath = RepoPathFactory.create('release', item.repoPath.path)
            
            echo "ID is " + item.repoPath.id
            if (!repositories.exists(destPath)) {
                echo "MISSING artifact in the release repo, scheduling async copy"
                // 提交异步任务执行复制逻辑
                def executions = ContextHelper.get().beanForType(org.artifactory.api.execution.Executions.class)
                executions.submitAsync("Copy artifact from stage-release to release", {
                    repositories.copy(sourcePath, destPath)
                })
            } else {
                echo "FOUND the artifact in the release repo so no need to copy"
            }
        }
    }
}

def echo(str) {
    log.warn "########################### " + str;
}

解决方案2:复制时直接禁用拦截器(更简洁)

Artifactory 5.x的Groovy API支持在复制操作时禁用拦截器,这样复制构件到release仓库时不会触发afterCreate钩子,从根源上避免循环触发和并发问题。修改复制那一行代码即可:

import org.artifactory.exception.CancelException
import org.artifactory.repo.RepoPathFactory

storage {
    afterCreate { item ->
        def SourcePath = RepoPathFactory.create('stage-release', item.repoPath.path)
        def DestPath = RepoPathFactory.create('release', item.repoPath.path)
        if (item.repoPath.repoKey == 'stage-release') {
            echo " ID is " + item.repoPath.id
            if (repositories.exists(DestPath)) {
                echo "FOUND the artifact in the release repo so no need to copy"
            } else {
                echo "MISSING artifact in the release repo so copying it over"
                // 添加参数禁用复制时的钩子触发
                repositories.copy(SourcePath, DestPath, [suppressInterceptors: true])
            }
        }
    }
}

def echo(str) {
    log.warn "########################### " + str;
}

为什么升级后才出现问题?

Artifactory升级到5.5.1后,插件系统的钩子执行机制做了并发安全的强化校验——升级前可能没有这么严格的检测,所以钩子的循环触发没有抛出异常,但实际上这种写法本身就存在线程安全隐患,只是之前没暴露出来而已。

内容的提问来源于stack exchange,提问作者Raj

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:10:08