All Downloads are FREE. Search and download functionalities are using the official Maven repository.

shaded.org.eclipse.aether.internal.impl.filter.GroupIdRemoteRepositoryFilterSource Maven / Gradle / Ivy

There is a newer version: 4.1.4
Show newest version
/*
 * 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 shaded.shaded.org.eclipse.aether.internal.impl.filter;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;

import org.eclipse.aether.MultiRuntimeException;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.artifact.Artifact;
import shaded.shaded.org.eclipse.aether.impl.RepositorySystemLifecycle;
import org.eclipse.aether.metadata.Metadata;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.resolution.ArtifactResult;
import shaded.shaded.org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilter;
import shaded.shaded.org.eclipse.aether.spi.resolution.ArtifactResolverPostProcessor;
import org.eclipse.aether.util.ConfigUtils;
import org.eclipse.aether.util.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static java.util.Objects.requireNonNull;

/**
 * Remote repository filter source filtering on G coordinate. It is backed by a file that lists all allowed groupIds
 * and groupId not present in this file are filtered out.
 * 

* The file can be authored manually: format is one groupId per line, comments starting with "#" (hash) amd empty lines * for structuring are supported. The file can also be pre-populated by "record" functionality of this filter. * When "recording", this filter will not filter out anything, but will instead populate the file with all encountered * groupIds. *

* The groupId file is expected on path "${basedir}/groupId-${repository.id}.txt". *

* The groupId file once loaded are cached in component, so in-flight groupId file change during component existence * are NOT noticed. * * @since 1.9.0 */ @Singleton @Named(GroupIdRemoteRepositoryFilterSource.NAME) public final class GroupIdRemoteRepositoryFilterSource extends RemoteRepositoryFilterSourceSupport implements ArtifactResolverPostProcessor { public static final String NAME = "groupId"; private static final String CONF_NAME_RECORD = "record"; static final String GROUP_ID_FILE_PREFIX = "groupId-"; static final String GROUP_ID_FILE_SUFFIX = ".txt"; private static final Logger LOGGER = LoggerFactory.getLogger(GroupIdRemoteRepositoryFilterSource.class); private final RepositorySystemLifecycle repositorySystemLifecycle; private final ConcurrentHashMap> rules; private final ConcurrentHashMap changedRules; private final AtomicBoolean onShutdownHandlerRegistered; @Inject public GroupIdRemoteRepositoryFilterSource(RepositorySystemLifecycle repositorySystemLifecycle) { super(NAME); this.repositorySystemLifecycle = requireNonNull(repositorySystemLifecycle); this.rules = new ConcurrentHashMap<>(); this.changedRules = new ConcurrentHashMap<>(); this.onShutdownHandlerRegistered = new AtomicBoolean(false); } @Override public RemoteRepositoryFilter getRemoteRepositoryFilter(RepositorySystemSession session) { if (isEnabled(session) && !isRecord(session)) { return new GroupIdFilter(session); } return null; } @Override public void postProcess(RepositorySystemSession session, List artifactResults) { if (isEnabled(session) && isRecord(session)) { if (onShutdownHandlerRegistered.compareAndSet(false, true)) { repositorySystemLifecycle.addOnSystemEndedHandler(this::saveRecordedLines); } for (ArtifactResult artifactResult : artifactResults) { if (artifactResult.isResolved() && artifactResult.getRepository() instanceof RemoteRepository) { Path filePath = filePath( getBasedir(session, false), artifactResult.getRepository().getId()); boolean newGroupId = rules.computeIfAbsent( filePath, f -> Collections.synchronizedSet(new TreeSet<>())) .add(artifactResult.getArtifact().getGroupId()); if (newGroupId) { changedRules.put(filePath, Boolean.TRUE); } } } } } /** * Returns the groupId path. The file and parents may not exist, this method merely calculate the path. */ private Path filePath(Path basedir, String remoteRepositoryId) { return basedir.resolve(GROUP_ID_FILE_PREFIX + remoteRepositoryId + GROUP_ID_FILE_SUFFIX); } private Set cacheRules(RepositorySystemSession session, RemoteRepository remoteRepository) { Path filePath = filePath(getBasedir(session, false), remoteRepository.getId()); return rules.computeIfAbsent(filePath, r -> { Set rules = loadRepositoryRules(filePath); if (rules != NOT_PRESENT) { LOGGER.info("Loaded {} groupId for remote repository {}", rules.size(), remoteRepository.getId()); } return rules; }); } private Set loadRepositoryRules(Path filePath) { if (Files.isReadable(filePath)) { try (BufferedReader reader = Files.newBufferedReader(filePath, StandardCharsets.UTF_8)) { TreeSet result = new TreeSet<>(); String groupId; while ((groupId = reader.readLine()) != null) { if (!groupId.startsWith("#") && !groupId.trim().isEmpty()) { result.add(groupId); } } return Collections.unmodifiableSet(result); } catch (IOException e) { throw new UncheckedIOException(e); } } return NOT_PRESENT; } private static final TreeSet NOT_PRESENT = new TreeSet<>(); private class GroupIdFilter implements RemoteRepositoryFilter { private final RepositorySystemSession session; private GroupIdFilter(RepositorySystemSession session) { this.session = session; } @Override public Result acceptArtifact(RemoteRepository remoteRepository, Artifact artifact) { return acceptGroupId(remoteRepository, artifact.getGroupId()); } @Override public Result acceptMetadata(RemoteRepository remoteRepository, Metadata metadata) { return acceptGroupId(remoteRepository, metadata.getGroupId()); } private Result acceptGroupId(RemoteRepository remoteRepository, String groupId) { Set groupIds = cacheRules(session, remoteRepository); if (NOT_PRESENT == groupIds) { return NOT_PRESENT_RESULT; } if (groupIds.contains(groupId)) { return new SimpleResult(true, "G:" + groupId + " allowed from " + remoteRepository); } else { return new SimpleResult(false, "G:" + groupId + " NOT allowed from " + remoteRepository); } } } private static final RemoteRepositoryFilter.Result NOT_PRESENT_RESULT = new SimpleResult(true, "GroupId file not present"); /** * Returns {@code true} if given session is recording. */ private boolean isRecord(RepositorySystemSession session) { return ConfigUtils.getBoolean(session, false, configPropKey(CONF_NAME_RECORD)); } /** * On-close handler that saves recorded rules, if any. */ private void saveRecordedLines() { if (changedRules.isEmpty()) { return; } ArrayList exceptions = new ArrayList<>(); for (Map.Entry> entry : rules.entrySet()) { Path filePath = entry.getKey(); if (changedRules.get(filePath) != Boolean.TRUE) { continue; } Set recordedLines = entry.getValue(); if (!recordedLines.isEmpty()) { try { TreeSet result = new TreeSet<>(); result.addAll(loadRepositoryRules(filePath)); result.addAll(recordedLines); LOGGER.info("Saving {} groupIds to '{}'", result.size(), filePath); FileUtils.writeFileWithBackup(filePath, p -> Files.write(p, result)); } catch (IOException e) { exceptions.add(e); } } } MultiRuntimeException.mayThrow("session save groupIds failure", exceptions); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy