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; 020 021import javax.inject.Inject; 022import javax.inject.Named; 023import javax.inject.Singleton; 024 025import org.eclipse.aether.ConfigurationProperties; 026import org.eclipse.aether.RepositorySystemSession; 027import org.eclipse.aether.repository.LocalRepository; 028import org.eclipse.aether.repository.LocalRepositoryManager; 029import org.eclipse.aether.repository.NoLocalRepositoryManagerException; 030import org.eclipse.aether.spi.localrepo.LocalRepositoryManagerFactory; 031import org.eclipse.aether.spi.remoterepo.RepositoryKeyFunctionFactory; 032import org.eclipse.aether.util.ConfigUtils; 033 034import static java.util.Objects.requireNonNull; 035 036/** 037 * Creates enhanced local repository managers for repository types {@code "default"} or {@code "" (automatic)}. Enhanced 038 * local repository manager is built upon the classical Maven 2.0 local repository structure but additionally keeps 039 * track of from what repositories a cached artifact was resolved. Resolution of locally cached artifacts will be 040 * rejected in case the current resolution request does not match the known source repositories of an artifact, thereby 041 * emulating physically separated artifact caches per remote repository. 042 */ 043@Singleton 044@Named(EnhancedLocalRepositoryManagerFactory.NAME) 045public class EnhancedLocalRepositoryManagerFactory implements LocalRepositoryManagerFactory { 046 public static final String NAME = "enhanced"; 047 048 static final String CONFIG_PROPS_PREFIX = ConfigurationProperties.PREFIX_LRM + NAME + "."; 049 050 /** 051 * Filename of the file in which to track the remote repositories. 052 * 053 * @configurationSource {@link RepositorySystemSession#getConfigProperties()} 054 * @configurationType {@link java.lang.String} 055 * @configurationDefaultValue {@link #DEFAULT_TRACKING_FILENAME} 056 */ 057 public static final String CONFIG_PROP_TRACKING_FILENAME = CONFIG_PROPS_PREFIX + "trackingFilename"; 058 059 public static final String DEFAULT_TRACKING_FILENAME = "_remote.repositories"; 060 061 /** 062 * Whether to verify that the real (on-disk) path of a locally cached artifact matches the requested path 063 * spelling before the artifact is used. On case-insensitive or case/normalization-preserving filesystems (the 064 * macOS and Windows defaults) a file cached for one set of coordinates also answers lookups for coordinates 065 * that differ only in case or Unicode normalization, while the repository tracking data is compared exactly: 066 * such an aliased file is treated as present-but-untracked and accepted with no download and no checksum 067 * verification, letting case-colliding coordinates poison distinct GAVs. When enabled (the default), an 068 * artifact whose on-disk path spelling differs from the requested one is treated as not present, forcing a 069 * proper download. Only the coordinate-derived part of the path is verified, so the base directory and the 070 * local path prefix directories (see {@link DefaultLocalPathPrefixComposerFactory}) may be symbolic links. 071 * Disable only if the local repository intentionally contains symbolic links below those directories. 072 * 073 * @configurationSource {@link RepositorySystemSession#getConfigProperties()} 074 * @configurationType {@link java.lang.Boolean} 075 * @configurationDefaultValue {@link #DEFAULT_VERIFY_REAL_PATH} 076 * @since 2.0.23 077 */ 078 public static final String CONFIG_PROP_VERIFY_REAL_PATH = CONFIG_PROPS_PREFIX + "verifyRealPath"; 079 080 public static final boolean DEFAULT_VERIFY_REAL_PATH = true; 081 082 /** 083 * Marks whether the local repository is meant to be shared (or was shared) with legacy Maven 3.9 or older 084 * versions. Maven 3.9 and older versions suffer from "impostor" problem, where artifact and metadata origin was 085 * tracked only by the remote repository ID, where two remote repositories may share same ID but different URLs, 086 * in fact they may be completely unrelated to each other (ID clash by mistake), or, it may be due some sort of 087 * "impostor" attempt, where a malicious repository may pretend like some other repository. 088 * Right now, we intentionally default to {@code true} to ease users transitioning, and Resolver 2 will retain 089 * this "old" behavior (will observe legacy tracking entries and will store remote metadata as before). But, 090 * at some point in the future, the default value will be changed to {@code false} (and same change is warmly 091 * recommended for modern Maven users, who do not intend to share local repository with older Maven versions). 092 * When this configuration set to {@code false}, the "repository key" is not ID only anymore, but is changed 093 * to {@code $id-sha1($url)} form, and this key is used in "origin tracking" entries and in caching remote 094 * Maven Repository Metadata XML files as well, guaranteeing they are not mixed in case of same IDs. 095 * 096 * @see ConfigurationProperties#REPOSITORY_SYSTEM_REPOSITORY_KEY_FUNCTION 097 * @see ConfigurationProperties#REPOSITORY_TRACKING_REPOSITORY_KEY_FUNCTION 098 * @configurationSource {@link RepositorySystemSession#getConfigProperties()} 099 * @configurationType {@link java.lang.Boolean} 100 * @configurationDefaultValue {@link #DEFAULT_LEGACY_LOCAL_REPOSITORY} 101 * @since 2.0.23 102 */ 103 public static final String CONFIG_PROP_LEGACY_LOCAL_REPOSITORY = CONFIG_PROPS_PREFIX + "legacyLocalRepository"; 104 105 public static final boolean DEFAULT_LEGACY_LOCAL_REPOSITORY = true; 106 107 private float priority = 10.0f; 108 109 private final LocalPathComposer localPathComposer; 110 111 private final TrackingFileManager trackingFileManager; 112 113 private final LocalPathPrefixComposerFactory localPathPrefixComposerFactory; 114 115 private final RepositoryKeyFunctionFactory repositoryKeyFunctionFactory; 116 117 @Inject 118 public EnhancedLocalRepositoryManagerFactory( 119 final LocalPathComposer localPathComposer, 120 final TrackingFileManager trackingFileManager, 121 final LocalPathPrefixComposerFactory localPathPrefixComposerFactory, 122 final RepositoryKeyFunctionFactory repositoryKeyFunctionFactory) { 123 this.localPathComposer = requireNonNull(localPathComposer); 124 this.trackingFileManager = requireNonNull(trackingFileManager); 125 this.localPathPrefixComposerFactory = requireNonNull(localPathPrefixComposerFactory); 126 this.repositoryKeyFunctionFactory = requireNonNull(repositoryKeyFunctionFactory); 127 } 128 129 @Override 130 public LocalRepositoryManager newInstance(RepositorySystemSession session, LocalRepository repository) 131 throws NoLocalRepositoryManagerException { 132 requireNonNull(session, "session cannot be null"); 133 requireNonNull(repository, "repository cannot be null"); 134 135 String trackingFilename = ConfigUtils.getString(session, "", CONFIG_PROP_TRACKING_FILENAME); 136 if (trackingFilename.isEmpty() 137 || trackingFilename.contains("/") 138 || trackingFilename.contains("\\") 139 || trackingFilename.contains("..")) { 140 trackingFilename = DEFAULT_TRACKING_FILENAME; 141 } 142 boolean legacyLocalRepository = 143 ConfigUtils.getBoolean(session, DEFAULT_LEGACY_LOCAL_REPOSITORY, CONFIG_PROP_LEGACY_LOCAL_REPOSITORY); 144 145 if ("".equals(repository.getContentType()) || "default".equals(repository.getContentType())) { 146 return new EnhancedLocalRepositoryManager( 147 repository.getBasePath(), 148 localPathComposer, 149 repositoryKeyFunctionFactory.trackingRepositoryKeyFunction(session), 150 trackingFilename, 151 legacyLocalRepository, 152 trackingFileManager, 153 localPathPrefixComposerFactory.createComposer(session)); 154 } else { 155 throw new NoLocalRepositoryManagerException(repository); 156 } 157 } 158 159 @Override 160 public float getPriority() { 161 return priority; 162 } 163 164 /** 165 * Sets the priority of this component. 166 * 167 * @param priority The priority. 168 * @return This component for chaining, never {@code null}. 169 */ 170 public EnhancedLocalRepositoryManagerFactory setPriority(float priority) { 171 this.priority = priority; 172 return this; 173 } 174}