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 /** 056 * SL enabled ctor. 057 * 058 * @deprecated for SL and testing purposes only. 059 */ 060 @Deprecated 061 public DefaultRemoteRepositoryFilterManager() { 062 this.sources = new HashMap<>(); 063 } 064 065 @Inject 066 public DefaultRemoteRepositoryFilterManager(Map<String, RemoteRepositoryFilterSource> sources) { 067 this.sources = requireNonNull(sources); 068 } 069 070 @Override 071 public RemoteRepositoryFilter getRemoteRepositoryFilter(RepositorySystemSession session) { 072 // use session specific key to distinguish between "derived" sessions 073 String instanceSpecificKey = INSTANCE_KEY + "." + session.hashCode(); 074 return (RemoteRepositoryFilter) session.getData().computeIfAbsent(instanceSpecificKey, () -> { 075 HashMap<String, RemoteRepositoryFilter> filters = new HashMap<>(); 076 for (Map.Entry<String, RemoteRepositoryFilterSource> entry : sources.entrySet()) { 077 RemoteRepositoryFilter filter = entry.getValue().getRemoteRepositoryFilter(session); 078 if (filter != null) { 079 filters.put(entry.getKey(), filter); 080 } 081 } 082 if (!filters.isEmpty()) { 083 return new Participants(filters); 084 } else { 085 return null; 086 } 087 }); 088 } 089 090 /** 091 * {@link RemoteRepositoryFilter} instance when there are participant filters present. It evaluates into result 092 * using {@link Consensus}. 093 */ 094 private static class Participants implements RemoteRepositoryFilter { 095 private final Map<String, RemoteRepositoryFilter> participants; 096 097 private Participants(Map<String, RemoteRepositoryFilter> participants) { 098 this.participants = Collections.unmodifiableMap(participants); 099 } 100 101 @Override 102 public RemoteRepositoryFilter.Result acceptArtifact(RemoteRepository remoteRepository, Artifact artifact) { 103 return new Consensus( 104 participants.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue() 105 .acceptArtifact(remoteRepository, artifact)))); 106 } 107 108 @Override 109 public RemoteRepositoryFilter.Result acceptMetadata(RemoteRepository remoteRepository, Metadata metadata) { 110 return new Consensus( 111 participants.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue() 112 .acceptMetadata(remoteRepository, metadata)))); 113 } 114 } 115 116 /** 117 * {@link RemoteRepositoryFilter.Result} based on "consensus". All participant have to "accept" to make this 118 * instance "accept". 119 */ 120 private static class Consensus implements RemoteRepositoryFilter.Result { 121 private final boolean accepted; 122 123 private final String reasoning; 124 125 Consensus(Map<String, RemoteRepositoryFilter.Result> results) { 126 this.accepted = results.values().stream().allMatch(RemoteRepositoryFilter.Result::isAccepted); 127 this.reasoning = results.values().stream() 128 .filter(r -> !r.isAccepted()) 129 .map(RemoteRepositoryFilter.Result::reasoning) 130 .collect(Collectors.joining("; ")); 131 } 132 133 @Override 134 public boolean isAccepted() { 135 return accepted; 136 } 137 138 @Override 139 public String reasoning() { 140 return reasoning; 141 } 142 } 143}