001package org.eclipse.aether.internal.impl.filter; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import javax.inject.Inject; 023import javax.inject.Named; 024import javax.inject.Singleton; 025 026import java.util.Collections; 027import java.util.HashMap; 028import java.util.Map; 029import java.util.stream.Collectors; 030 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 052 implements RemoteRepositoryFilterManager 053{ 054 private static final String INSTANCE_KEY = DefaultRemoteRepositoryFilterManager.class.getName() + ".instance"; 055 056 private final Map<String, RemoteRepositoryFilterSource> sources; 057 058 /** 059 * SL enabled ctor. 060 * 061 * @deprecated for SL and testing purposes only. 062 */ 063 @Deprecated 064 public DefaultRemoteRepositoryFilterManager() 065 { 066 this.sources = new HashMap<>(); 067 } 068 069 @Inject 070 public DefaultRemoteRepositoryFilterManager( Map<String, RemoteRepositoryFilterSource> sources ) 071 { 072 this.sources = requireNonNull( sources ); 073 } 074 075 @Override 076 public RemoteRepositoryFilter getRemoteRepositoryFilter( RepositorySystemSession session ) 077 { 078 return (RemoteRepositoryFilter) session.getData().computeIfAbsent( INSTANCE_KEY, () -> 079 { 080 HashMap<String, RemoteRepositoryFilter> filters = new HashMap<>(); 081 for ( Map.Entry<String, RemoteRepositoryFilterSource> entry : sources.entrySet() ) 082 { 083 RemoteRepositoryFilter filter = entry.getValue().getRemoteRepositoryFilter( session ); 084 if ( filter != null ) 085 { 086 filters.put( entry.getKey(), filter ); 087 } 088 } 089 if ( !filters.isEmpty() ) 090 { 091 return new Participants( filters ); 092 } 093 else 094 { 095 return null; 096 } 097 } 098 ); 099 } 100 101 /** 102 * {@link RemoteRepositoryFilter} instance when there are participant filters present. It evaluates into result 103 * using {@link Consensus}. 104 */ 105 private static class Participants implements RemoteRepositoryFilter 106 { 107 private final Map<String, RemoteRepositoryFilter> participants; 108 109 private Participants( Map<String, RemoteRepositoryFilter> participants ) 110 { 111 this.participants = Collections.unmodifiableMap( participants ); 112 } 113 114 @Override 115 public RemoteRepositoryFilter.Result acceptArtifact( RemoteRepository remoteRepository, Artifact artifact ) 116 { 117 return new Consensus( participants.entrySet().stream() 118 .collect( Collectors.toMap( 119 Map.Entry::getKey, 120 e -> e.getValue().acceptArtifact( remoteRepository, artifact ) ) ) ); 121 } 122 123 @Override 124 public RemoteRepositoryFilter.Result acceptMetadata( RemoteRepository remoteRepository, Metadata metadata ) 125 { 126 return new Consensus( participants.entrySet().stream() 127 .collect( Collectors.toMap( 128 Map.Entry::getKey, 129 e -> e.getValue().acceptMetadata( remoteRepository, metadata ) ) ) ); 130 } 131 } 132 133 /** 134 * {@link RemoteRepositoryFilter.Result} based on "consensus". All participant have to "accept" to make this 135 * instance "accept". 136 */ 137 private static class Consensus implements RemoteRepositoryFilter.Result 138 { 139 private final boolean accepted; 140 141 private final String reasoning; 142 143 Consensus( Map<String, RemoteRepositoryFilter.Result> results ) 144 { 145 this.accepted = results.values().stream().allMatch( RemoteRepositoryFilter.Result::isAccepted ); 146 this.reasoning = results.values().stream().filter( r -> !r.isAccepted() ).map( 147 RemoteRepositoryFilter.Result::reasoning ).collect( 148 Collectors.joining( "; " ) ); 149 } 150 151 @Override 152 public boolean isAccepted() 153 { 154 return accepted; 155 } 156 157 @Override 158 public String reasoning() 159 { 160 return reasoning; 161 } 162 } 163}