001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.eclipse.aether.internal.impl.filter; 020 021import javax.inject.Inject; 022import javax.inject.Named; 023import javax.inject.Singleton; 024 025import java.util.Collections; 026import java.util.HashMap; 027import java.util.Map; 028import java.util.stream.Collectors; 029 030import org.eclipse.aether.RepositorySystemSession; 031import org.eclipse.aether.artifact.Artifact; 032import org.eclipse.aether.impl.RemoteRepositoryFilterManager; 033import org.eclipse.aether.metadata.Metadata; 034import org.eclipse.aether.repository.RemoteRepository; 035import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilter; 036import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilterSource; 037 038import static java.util.Objects.requireNonNull; 039 040/** 041 * Default implementation of {@link RemoteRepositoryFilterManager}, it always returns a {@link RemoteRepositoryFilter} 042 * instance, even if no filter sources enabled/registered (then "always allow" instance). 043 * <p> 044 * The created {@link RemoteRepositoryFilter} instance is created once per session and cached. 045 * 046 * @since 1.9.0 047 */ 048@Singleton 049@Named 050public final class DefaultRemoteRepositoryFilterManager implements RemoteRepositoryFilterManager { 051 private static final String INSTANCE_KEY = DefaultRemoteRepositoryFilterManager.class.getName() + ".instance"; 052 053 private final Map<String, RemoteRepositoryFilterSource> sources; 054 055 @Inject 056 public DefaultRemoteRepositoryFilterManager(Map<String, RemoteRepositoryFilterSource> sources) { 057 this.sources = requireNonNull(sources); 058 } 059 060 @Override 061 public RemoteRepositoryFilter getRemoteRepositoryFilter(RepositorySystemSession session) { 062 // use session specific key to distinguish between "derived" sessions 063 String instanceSpecificKey = INSTANCE_KEY + "." + session.hashCode(); 064 return (RemoteRepositoryFilter) session.getData().computeIfAbsent(instanceSpecificKey, () -> { 065 HashMap<String, RemoteRepositoryFilter> filters = new HashMap<>(); 066 for (Map.Entry<String, RemoteRepositoryFilterSource> entry : sources.entrySet()) { 067 RemoteRepositoryFilter filter = entry.getValue().getRemoteRepositoryFilter(session); 068 if (filter != null) { 069 filters.put(entry.getKey(), filter); 070 } 071 } 072 if (!filters.isEmpty()) { 073 return new Participants(filters); 074 } else { 075 return null; 076 } 077 }); 078 } 079 080 /** 081 * {@link RemoteRepositoryFilter} instance when there are participant filters present. It evaluates into result 082 * using {@link Consensus}. 083 */ 084 private static class Participants implements RemoteRepositoryFilter { 085 private final Map<String, RemoteRepositoryFilter> participants; 086 087 private Participants(Map<String, RemoteRepositoryFilter> participants) { 088 this.participants = Collections.unmodifiableMap(participants); 089 } 090 091 @Override 092 public RemoteRepositoryFilter.Result acceptArtifact(RemoteRepository remoteRepository, Artifact artifact) { 093 return new Consensus( 094 participants.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue() 095 .acceptArtifact(remoteRepository, artifact)))); 096 } 097 098 @Override 099 public RemoteRepositoryFilter.Result acceptMetadata(RemoteRepository remoteRepository, Metadata metadata) { 100 return new Consensus( 101 participants.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue() 102 .acceptMetadata(remoteRepository, metadata)))); 103 } 104 } 105 106 /** 107 * {@link RemoteRepositoryFilter.Result} based on "consensus". All participant have to "accept" to make this 108 * instance "accept". 109 */ 110 private static class Consensus implements RemoteRepositoryFilter.Result { 111 private final boolean accepted; 112 113 private final String reasoning; 114 115 Consensus(Map<String, RemoteRepositoryFilter.Result> results) { 116 this.accepted = results.values().stream().allMatch(RemoteRepositoryFilter.Result::isAccepted); 117 this.reasoning = results.values().stream() 118 .filter(r -> !r.isAccepted()) 119 .map(RemoteRepositoryFilter.Result::reasoning) 120 .collect(Collectors.joining("; ")); 121 } 122 123 @Override 124 public boolean isAccepted() { 125 return accepted; 126 } 127 128 @Override 129 public String reasoning() { 130 return reasoning; 131 } 132 } 133}