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.Keys; 031import org.eclipse.aether.RepositorySystemSession; 032import org.eclipse.aether.artifact.Artifact; 033import org.eclipse.aether.impl.RemoteRepositoryFilterManager; 034import org.eclipse.aether.metadata.Metadata; 035import org.eclipse.aether.repository.RemoteRepository; 036import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilter; 037import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilterSource; 038 039import static java.util.Objects.requireNonNull; 040 041/** 042 * Default implementation of {@link RemoteRepositoryFilterManager}, it always returns a {@link RemoteRepositoryFilter} 043 * instance, even if no filter sources enabled/registered (then "always allow" instance). 044 * <p> 045 * The created {@link RemoteRepositoryFilter} instance is created once per session and cached. 046 * 047 * @since 1.9.0 048 */ 049@Singleton 050@Named 051public final class DefaultRemoteRepositoryFilterManager implements RemoteRepositoryFilterManager { 052 private final Map<String, RemoteRepositoryFilterSource> sources; 053 054 @Inject 055 public DefaultRemoteRepositoryFilterManager(Map<String, RemoteRepositoryFilterSource> sources) { 056 this.sources = requireNonNull(sources); 057 } 058 059 @Override 060 public RemoteRepositoryFilter getRemoteRepositoryFilter(RepositorySystemSession session) { 061 // use session specific key to distinguish between "derived" sessions 062 Object sessionDiscriminator; 063 if (session instanceof RepositorySystemSession.CloseableSession) { 064 sessionDiscriminator = ((RepositorySystemSession.CloseableSession) session).sessionId(); 065 } else { 066 sessionDiscriminator = System.identityHashCode(session); 067 } 068 return (RemoteRepositoryFilter) session.getData() 069 .computeIfAbsent(Keys.of(DefaultRemoteRepositoryFilterManager.class, sessionDiscriminator), () -> { 070 HashMap<String, RemoteRepositoryFilter> filters = new HashMap<>(); 071 for (Map.Entry<String, RemoteRepositoryFilterSource> entry : sources.entrySet()) { 072 RemoteRepositoryFilter filter = entry.getValue().getRemoteRepositoryFilter(session); 073 if (filter != null) { 074 filters.put(entry.getKey(), filter); 075 } 076 } 077 if (!filters.isEmpty()) { 078 return new Participants(filters); 079 } else { 080 return null; 081 } 082 }); 083 } 084 085 /** 086 * {@link RemoteRepositoryFilter} instance when there are participant filters present. It evaluates into result 087 * using {@link Consensus}. 088 */ 089 private static class Participants implements RemoteRepositoryFilter { 090 private final Map<String, RemoteRepositoryFilter> participants; 091 092 private Participants(Map<String, RemoteRepositoryFilter> participants) { 093 this.participants = Collections.unmodifiableMap(participants); 094 } 095 096 @Override 097 public RemoteRepositoryFilter.Result acceptArtifact(RemoteRepository remoteRepository, Artifact artifact) { 098 return new Consensus(participants.entrySet().stream() 099 .collect(Collectors.toMap( 100 Map.Entry::getKey, e -> e.getValue().acceptArtifact(remoteRepository, artifact)))); 101 } 102 103 @Override 104 public RemoteRepositoryFilter.Result acceptMetadata(RemoteRepository remoteRepository, Metadata metadata) { 105 return new Consensus(participants.entrySet().stream() 106 .collect(Collectors.toMap( 107 Map.Entry::getKey, e -> e.getValue().acceptMetadata(remoteRepository, metadata)))); 108 } 109 } 110 111 /** 112 * {@link RemoteRepositoryFilter.Result} based on "consensus". All participant have to "accept" to make this 113 * instance "accept". 114 */ 115 private static class Consensus implements RemoteRepositoryFilter.Result { 116 private final boolean accepted; 117 118 private final String reasoning; 119 120 Consensus(Map<String, RemoteRepositoryFilter.Result> results) { 121 this.accepted = results.values().stream().allMatch(RemoteRepositoryFilter.Result::isAccepted); 122 this.reasoning = results.values().stream() 123 .filter(r -> !r.isAccepted()) 124 .map(RemoteRepositoryFilter.Result::reasoning) 125 .collect(Collectors.joining("; ")); 126 } 127 128 @Override 129 public boolean isAccepted() { 130 return accepted; 131 } 132 133 @Override 134 public String reasoning() { 135 return reasoning; 136 } 137 } 138}