标签搜索

目 录CONTENT

文章目录

nexus插件nexus-tag-plugin源码修改

陈铭
2021-08-10 / 0 评论 / 0 点赞 / 113 阅读 / 1,498 字 / 正在检测是否收录...

源码阅读

我们先读一下插件源码(Github

层级结构

nexus插件本质上也是个maven项目,最终打成Kar包给nexus使用
image

dto包

AssociatedComponent: 封装接口调用时传过来的制品属性,可以看到成员变量里面group不可以为null,对于pypi和docker制品(group只能是null),无法打标签。而作者这么定义group也是受nexus内部api限制。

@ComponentExists
public class AssociatedComponent {

    @NotNull(message = "Components repository can't be null.")
    @NotBlank
    private String repository;

    @NotNull
    private String group;

    @NotNull(message = "Components name can't be null.")
    @NotBlank
    private String name;

    private String version;
}

Tag:tag标签的pojo对象,我们调用tag的查询接口时,tag封装成该对象。
TagDefinition:tag的定义,我们调用tag的新增接口时,传参会封装成该对象。

exception包

底下各类顾名思义,对应出现各种错误时throw的异常

根目录

重点就说一个类TagRestResource,类似于SpringMVC的Controller,定义各种接口的

@Named
@Singleton
@Path(V1_API_PREFIX + "/tags")
public class TagRestResource extends ComponentSupport implements Resource, TagRestResourceDoc {

    private final TagStore tagStore;
    private final Validator validator;

    @Inject
    public TagRestResource(TagStore tagStore, Validator validator) {
        this.tagStore = tagStore;
        this.validator = validator;
    }

    @GET
    @Path("/{name}")
    @Produces(MediaType.APPLICATION_JSON)
    @Override
    public Tag getByName(@PathParam("name") String name) {
        return tagStore.getByName(name);
    }

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Override
    public List<Tag> list(@QueryParam("attributes") String attributes) {
        Map<String, String> attributeMap = decodeAttributes(attributes);
        List<Tag> tags = tagStore.search(attributeMap);
        log.info("Tag search for attributes={}={}", attributes, tags);
        return tags;
    }

    /**
     * 新增tag
     */
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    @Override
    public Tag add(TagDefinition definition) {
		//新增tag时,会验证传过来的制品属性能不能匹配到制品
        validate(definition);
		// 储存tag
        return tagStore.addOrUpdate(definition);
    }
	....
		
    private void validate(Object object) {
		// 调用nexus内部逻辑匹配制品
        Set<ConstraintViolation<Object>> violations = validator.validate(object);
        if (!violations.isEmpty()) {
            throw new ConstraintViolationException("Invalid tag.", violations);
        }
    }
}

validation包

TagRestResource类的validate方法调用nexus内部逻辑匹配制品,最后会调用ComponentExistsValidator类

@Named
@Singleton
public class ComponentExistsValidator extends ConstraintValidatorSupport<ComponentExists, AssociatedComponent> {

    private final RepositoryManager repositoryManager;
    private final ComponentStore componentStore;

    @Inject
    public ComponentExistsValidator(RepositoryManager repositoryManager, ComponentStore componentStore) {
        this.repositoryManager = repositoryManager;
        this.componentStore = componentStore;
    }

	// 开始匹配制品
    @Override
    public boolean isValid(AssociatedComponent value, ConstraintValidatorContext context) {
		// 获取tag传过来的仓库
        Repository repository = repositoryManager.get(value.getRepository());
		// 仓库是否存在??
        if (repository == null) {
            log.info("Component {} is invalid as repository does not exists.", value);
            return false;
        }
		// 封装tag传过来的制品的各种属性
        Map<String, String> versionAttribute = new HashMap<>();
        if (value.getVersion() != null && !value.getVersion().isEmpty()) {
            versionAttribute.put("version", value.getVersion());
        }

		// 此处调用nexus内部逻辑,用封装的属性寻找制品
        List<Component> founds = componentStore.getAllMatchingComponents(repository, value.getGroup(), value.getName(),
                versionAttribute);
        log.info("Components found for {}: {}", value, founds);
        return !founds.isEmpty();
    }
}

那我们就找一下getAllMatchingComponents这个方法做了什么?

	// 该方法在org.sonatype.nexus.repository.storage下
    public List<Component> getAllMatchingComponents(Repository repository, String group, String name, Map<String, String> formatAttributes) {
		// nexus在寻找匹配的制品时,要求各种属性不可以为null,才继续匹配
		// 这就是为什么作者写插件时候AssociatedComponent的group设置@NotNull
        Preconditions.checkNotNull(repository);
        Preconditions.checkNotNull(group);
        Preconditions.checkNotNull(name);
        Preconditions.checkNotNull(formatAttributes);
        Throwable var6 = null;
        Object var7 = null;
		
		....
    }

至此,我们知道了,这个插件缺陷蛮大的,兼容性不好。作者自己也在源码中的TODO说目前只支持maven制品库

源码修改

首先明确一下思路,我们由于匹配时group非null,导致插件无法使用。那么我们可以转变思路,我们可以利用nexus的searchAPI进行制品的查找(nexus有很多开发接口供调用)。所以我们小小修改一下TagRestResource类,也就是修改一下validate方法,让该方法不去调nexus的api,而是调searchAPI,该API返回json我们转换成pojo,并判断pojo是否为null且唯一来确认制品的匹配
当然,源码里面还有其他的修改,都是比较简单的逻辑,这里放上源码

public class TagRestResource extends ComponentSupport implements Resource, TagRestResourceDoc {

    private final TagStore tagStore;
    private final Validator validator;

    private final String NexusProtocol = "http://";
    private final String NexusHost = "xxx.xxx.xxx.xxx:xxx";
    private final String NexusSearchPath = "/service/rest/v1/search?";

    private final static ObjectMapper objectMapper = new ObjectMapper();
    static {
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }
	
	......
	
    /**
     * 检验流程:
     *     获取制品信息(POST请求体) -->
     *     调用nexusAPI搜索匹配的制品 -->
     *     判断是否唯一匹配
     */
    private void validate(Object object) {

        if (object instanceof TagDefinition) {
            TagDefinition tagDefinition = (TagDefinition) object;
            String uri = null;
            WebTarget target = null;
            for (AssociatedComponent component : tagDefinition.getComponents()) {
                String name = component.getName();
                String group = component.getGroup();
                String repository = component.getRepository();
                String version = component.getVersion();

                searchMatchedComponent(repository, name, version, group);
            }
        } else if (object instanceof TagCloneRequest) {
            // 不涉及制品匹配,用原有逻辑
            Set<ConstraintViolation<Object>> violations = validator.validate(object);
            if (!violations.isEmpty()) {
                throw new ConstraintViolationException("Invalid tag.", violations);
            }
        }
    }

    /**
     * 为什么用原生JDK发起http请求?
     * 因为插件依赖的模块在安装插件后都会成为一个bundle,就比如本插件依赖了Jackson-core、Jackson-annotations、jackson-databind
     * 我们安装插件后实际上新增了4个bundle,也就是三面三个模块加tag插件本身
     * 所以我选择尽量避免依赖太多额外的模块,避免可能的冲突
     */
    private void searchMatchedComponent(String repository, String name, String version, String group) {
        try {

            String searchUrl = NexusProtocol + NexusHost + NexusSearchPath;
            if (group != null) {
                searchUrl += "name=" + name + "&group=" + group + "&version=" + version + "&repository=" + repository;
            } else {
                searchUrl += "name=" + name + "&version=" + version + "&repository=" + repository;
            }

            URL url = new URL(searchUrl);
            HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
            httpURLConnection.setRequestMethod("GET");

            InputStreamReader inputStreamReader = new InputStreamReader(httpURLConnection.getInputStream());
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String json = "";

            while (true) {
                String readLine = bufferedReader.readLine();
                if (readLine == null) {
                    break;
                }
                json += readLine;
            }
            bufferedReader.close();

            try {
                SearchResult searchResult = objectMapper.readValue(json, SearchResult.class);
                // 当且仅当存在唯一对应的制品则检验通过
                if (searchResult == null || searchResult.getItems().size() != 1) {
                    throw new ServerException("Cannot match the component by json :" + json);
                }
            } catch (JsonProcessingException e) {
                throw new RuntimeException("json data erro:" + json);
            }

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (ProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


}

打包测试

参考《为nexus安装插件:nexus-tag-plugin为例》这篇博客,安装并测试,成功(截图略)

0

评论区