使用Java代码删除Nexus已上传的组件

背景

Nexus是一个强大的Maven仓库管理器。项目迭代中,会产生很多版本的maven组件上传至Nexus私服。对于不需要的组件,需要删除。如果在页面上逐个删除对于组件众多、版本众多的项目来说无疑是灾难

Nexus提供的API

页面访问 {nexus_host}/service/rest/swagger.json ,可以看到Nexus提供的所有API

image.png

截图并不是所有的接口,可以自己访问接口查看返回的json

image.png

接口详情中包含接口的描述、参数、响应状态码等

需要使用的接口

根据我的需求,我需要使用两个接口,组件查询接口与组件删除接口,如下图所示:

组件查询:


image.png

组件删除:


image.png

详细代码

依赖

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
        </dependency>

Entity

public class Checksum {

    private String sha1;
    private String md5;

    public String getSha1() {
        return sha1;
    }

    public Checksum setSha1(String sha1) {
        this.sha1 = sha1;
        return this;
    }

    public String getMd5() {
        return md5;
    }

    public Checksum setMd5(String md5) {
        this.md5 = md5;
        return this;
    }
}
public class Asset {

    private String downloadUrl;
    private String path;
    private String id;
    private String repository;
    private String format;
    private Checksum checksum;

    public String getDownloadUrl() {
        return downloadUrl;
    }

    public Asset setDownloadUrl(String downloadUrl) {
        this.downloadUrl = downloadUrl;
        return this;
    }

    public String getPath() {
        return path;
    }

    public Asset setPath(String path) {
        this.path = path;
        return this;
    }

    public String getId() {
        return id;
    }

    public Asset setId(String id) {
        this.id = id;
        return this;
    }

    public String getRepository() {
        return repository;
    }

    public Asset setRepository(String repository) {
        this.repository = repository;
        return this;
    }

    public String getFormat() {
        return format;
    }

    public Asset setFormat(String format) {
        this.format = format;
        return this;
    }

    public Checksum getChecksum() {
        return checksum;
    }

    public Asset setChecksum(Checksum checksum) {
        this.checksum = checksum;
        return this;
    }
}
public class Module {

    private String id;
    private String repository;
    private String format;
    private String group;
    private String name;
    private String version;

    private List<Asset> assets;

    public String getId() {
        return id;
    }

    public Module setId(String id) {
        this.id = id;
        return this;
    }

    public String getRepository() {
        return repository;
    }

    public Module setRepository(String repository) {
        this.repository = repository;
        return this;
    }

    public String getFormat() {
        return format;
    }

    public Module setFormat(String format) {
        this.format = format;
        return this;
    }

    public String getGroup() {
        return group;
    }

    public Module setGroup(String group) {
        this.group = group;
        return this;
    }

    public String getName() {
        return name;
    }

    public Module setName(String name) {
        this.name = name;
        return this;
    }

    public String getVersion() {
        return version;
    }

    public Module setVersion(String version) {
        this.version = version;
        return this;
    }

    public List<Asset> getAssets() {
        return assets;
    }

    public Module setAssets(List<Asset> assets) {
        this.assets = assets;
        return this;
    }
}
public class Service {
    
    private String groupId;
    private String artifactId;

    public String getGroupId() {
        return groupId;
    }

    public Service setGroupId(String groupId) {
        this.groupId = groupId;
        return this;
    }

    public String getArtifactId() {
        return artifactId;
    }

    public Service setArtifactId(String artifactId) {
        this.artifactId = artifactId;
        return this;
    }
}

主要代码

public class Clean {

    private static final OkHttpClient HTTP_CLIENT = new OkHttpClient.Builder().connectTimeout(10, TimeUnit.SECONDS).readTimeout(10, TimeUnit.SECONDS).build();
    private static final Logger LOGGER = LoggerFactory.getLogger(Clean.class);

    private static final String NEXUS_HOST = "http://nexus.demo.com";
    // 账户需要有查询和删除权限
    private static final String NEXUS_USERNAME = "admin";
    private static final String NEXUS_PASSWORD = "admin";
    private static final String NEXUS_REPOSITORY = "demo-release";

    public static void main(String[] args) {
        // 需要删除的组件坐标信息
        List<Service> serviceList = new ArrayList<>();
        serviceList.add(new Service().setGroupId("org.demo").setArtifactId("demo-service"));
        // 需要删除的组件版本
        List<String> versionList = new ArrayList<>();
        versionList.add("0.1.0.RELEASE");

        for(Service service : serviceList) {
            List<Module> moduleList = new ArrayList<>();
            search(NEXUS_HOST, NEXUS_USERNAME, NEXUS_PASSWORD, NEXUS_REPOSITORY, service.getGroupId(), service.getArtifactId(), null, moduleList);
            for (Module module : moduleList) {
                if (versionList.contains(module.getVersion())) {
                    // 版本匹配执行删除
                    delete(NEXUS_HOST, NEXUS_USERNAME, NEXUS_PASSWORD, module);
                }
            }
        }
    }

    private static void search(String host, String username, String password, String repository, String groupId, String artifactId, String token, List<Module> list) {
        String url = String.format("%s/service/rest/v1/search?repository=%s&group=%s&name=%s", host, repository, groupId, artifactId);
        if (StringUtils.hasText(token)) {
            // 拼接上次查询返回的continuationToken,查询下一页
            url = url + "&continuationToken=" + token;
        }
        Request request = new Request.Builder()
                .addHeader("Authorization", getToken(username, password))
                .addHeader("Connection", "keep-alive")
                .url(url)
                .get()
                .build();
        Call call = HTTP_CLIENT.newCall(request);
        try (Response response = call.execute()) {
            if (response.code() == 200 && response.body() != null) {
                JSONObject jsonObject = JSONObject.parseObject(response.body().string());
                List<Module> result = jsonObject.getObject("items", new TypeReference<List<Module>>() {
                });
                if (CollectionUtils.isEmpty(result)) {
                    return;
                }
                list.addAll(result);
                String conToken = jsonObject.getString("continuationToken");
                if (!StringUtils.hasText(conToken) || "null".equals(conToken)) {
                    return;
                }
                // 携带continuationToken递归查询下一页
                search(host, username, password, repository, groupId, artifactId, conToken, list);
            } else {
                LOGGER.error("组件搜索出错,http响应:{}", response.body());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void delete(String host, String username, String password, Module module) {
        LOGGER.info("执行删除: {}", module.getVersion());
        String url = String.format("%s/service/rest/v1/components/%s", host, module.getId());
        Request request = new Request.Builder()
                .addHeader("Authorization", getToken(username, password))
                .addHeader("Connection", "keep-alive")
                .url(url)
                .delete()
                .build();
        Call call = HTTP_CLIENT.newCall(request);
        try (Response response = call.execute()) {
            if (response.code() == 204) {
                LOGGER.info("删除成功!");
            } else {
                LOGGER.error("删除失败! 状态码:{}", response.code());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static String getToken(String name, String password) {
        String authString = name + ":" + password;
        byte[] authEncBytes = Base64.getEncoder().encode(authString.getBytes(StandardCharsets.UTF_8));
        return "Basic " + new String(authEncBytes);
    }
}

参考

Nexus官方文档

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容