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 org.eclipse.aether.RepositorySystemSession;
022import org.eclipse.aether.artifact.Artifact;
023import org.eclipse.aether.metadata.Metadata;
024import org.eclipse.aether.repository.RemoteRepository;
025import org.eclipse.aether.repository.RepositoryKeyFunction;
026import org.eclipse.aether.util.ConfigUtils;
027import org.eclipse.aether.util.PathUtils;
028
029/**
030 * Support class for {@link LocalPathPrefixComposerFactory} implementations: it predefines and makes re-usable
031 * common configuration getters, and defines a support class for {@link LocalPathPrefixComposer} carrying same
032 * configuration and providing default implementation for all methods.
033 * <p>
034 * Implementors should extend this class to implement custom split strategies. If one needs to alter default
035 * configuration, they should override any configuration getter from this class.
036 *
037 * @see DefaultLocalPathPrefixComposerFactory
038 * @since 1.8.1
039 */
040public abstract class LocalPathPrefixComposerFactorySupport implements LocalPathPrefixComposerFactory {
041
042    /**
043     * Whether LRM should split local and remote artifacts.
044     *
045     * @configurationSource {@link RepositorySystemSession#getConfigProperties()}
046     * @configurationType {@link java.lang.Boolean}
047     * @configurationDefaultValue {@link #DEFAULT_SPLIT}
048     */
049    public static final String CONFIG_PROP_SPLIT = EnhancedLocalRepositoryManagerFactory.CONFIG_PROPS_PREFIX + "split";
050
051    public static final boolean DEFAULT_SPLIT = false;
052
053    /**
054     * The prefix to use for locally installed artifacts.
055     *
056     * @configurationSource {@link RepositorySystemSession#getConfigProperties()}
057     * @configurationType {@link java.lang.String}
058     * @configurationDefaultValue {@link #DEFAULT_LOCAL_PREFIX}
059     */
060    public static final String CONFIG_PROP_LOCAL_PREFIX =
061            EnhancedLocalRepositoryManagerFactory.CONFIG_PROPS_PREFIX + "localPrefix";
062
063    public static final String DEFAULT_LOCAL_PREFIX = "installed";
064
065    /**
066     * Whether locally installed artifacts should be split by version (release/snapshot).
067     *
068     * @configurationSource {@link RepositorySystemSession#getConfigProperties()}
069     * @configurationType {@link java.lang.Boolean}
070     * @configurationDefaultValue {@link #DEFAULT_SPLIT_LOCAL}
071     */
072    public static final String CONFIG_PROP_SPLIT_LOCAL =
073            EnhancedLocalRepositoryManagerFactory.CONFIG_PROPS_PREFIX + "splitLocal";
074
075    public static final boolean DEFAULT_SPLIT_LOCAL = false;
076
077    /**
078     * The prefix to use for remotely cached artifacts.
079     *
080     * @configurationSource {@link RepositorySystemSession#getConfigProperties()}
081     * @configurationType {@link java.lang.String}
082     * @configurationDefaultValue {@link #DEFAULT_REMOTE_PREFIX}
083     */
084    public static final String CONFIG_PROP_REMOTE_PREFIX =
085            EnhancedLocalRepositoryManagerFactory.CONFIG_PROPS_PREFIX + "remotePrefix";
086
087    public static final String DEFAULT_REMOTE_PREFIX = "cached";
088
089    /**
090     * Whether cached artifacts should be split by version (release/snapshot).
091     *
092     * @configurationSource {@link RepositorySystemSession#getConfigProperties()}
093     * @configurationType {@link java.lang.Boolean}
094     * @configurationDefaultValue {@link #DEFAULT_SPLIT_REMOTE}
095     */
096    public static final String CONFIG_PROP_SPLIT_REMOTE =
097            EnhancedLocalRepositoryManagerFactory.CONFIG_PROPS_PREFIX + "splitRemote";
098
099    public static final boolean DEFAULT_SPLIT_REMOTE = false;
100
101    /**
102     * Whether cached artifacts should be split by origin repository (repository ID).
103     *
104     * @configurationSource {@link RepositorySystemSession#getConfigProperties()}
105     * @configurationType {@link java.lang.Boolean}
106     * @configurationDefaultValue {@link #DEFAULT_SPLIT_REMOTE_REPOSITORY}
107     */
108    public static final String CONFIG_PROP_SPLIT_REMOTE_REPOSITORY =
109            EnhancedLocalRepositoryManagerFactory.CONFIG_PROPS_PREFIX + "splitRemoteRepository";
110
111    public static final boolean DEFAULT_SPLIT_REMOTE_REPOSITORY = false;
112
113    /**
114     * For cached artifacts, if both splitRemote and splitRemoteRepository are set to true sets the splitting order:
115     * by default it is repositoryId/version (false) or version/repositoryId (true)
116     *
117     * @configurationSource {@link RepositorySystemSession#getConfigProperties()}
118     * @configurationType {@link java.lang.Boolean}
119     * @configurationDefaultValue {@link #DEFAULT_SPLIT_REMOTE_REPOSITORY_LAST}
120     */
121    public static final String CONFIG_PROP_SPLIT_REMOTE_REPOSITORY_LAST =
122            EnhancedLocalRepositoryManagerFactory.CONFIG_PROPS_PREFIX + "splitRemoteRepositoryLast";
123
124    public static final boolean DEFAULT_SPLIT_REMOTE_REPOSITORY_LAST = false;
125
126    /**
127     * The prefix to use for release artifacts.
128     *
129     * @configurationSource {@link RepositorySystemSession#getConfigProperties()}
130     * @configurationType {@link java.lang.String}
131     * @configurationDefaultValue {@link #DEFAULT_RELEASES_PREFIX}
132     */
133    public static final String CONFIG_PROP_RELEASES_PREFIX =
134            EnhancedLocalRepositoryManagerFactory.CONFIG_PROPS_PREFIX + "releasesPrefix";
135
136    public static final String DEFAULT_RELEASES_PREFIX = "releases";
137
138    /**
139     * The prefix to use for snapshot artifacts.
140     *
141     * @configurationSource {@link RepositorySystemSession#getConfigProperties()}
142     * @configurationType {@link java.lang.String}
143     * @configurationDefaultValue {@link #DEFAULT_SNAPSHOTS_PREFIX}
144     */
145    public static final String CONFIG_PROP_SNAPSHOTS_PREFIX =
146            EnhancedLocalRepositoryManagerFactory.CONFIG_PROPS_PREFIX + "snapshotsPrefix";
147
148    public static final String DEFAULT_SNAPSHOTS_PREFIX = "snapshots";
149
150    // Legacy support: properties were renamed in Resolver 2.0.x, but we should support 1.9.x properties as well
151    // These below are Resolver 1.9.x properties, are undocumented and shall be removed with Resolver 2.1.x (or later).
152
153    private static final String R1_CONF_PROP_SPLIT = "aether.enhancedLocalRepository.split";
154
155    private static final String R1_CONF_PROP_LOCAL_PREFIX = "aether.enhancedLocalRepository.localPrefix";
156
157    private static final String R1_CONF_PROP_SPLIT_LOCAL = "aether.enhancedLocalRepository.splitLocal";
158
159    private static final String R1_CONF_PROP_REMOTE_PREFIX = "aether.enhancedLocalRepository.remotePrefix";
160
161    private static final String R1_CONF_PROP_SPLIT_REMOTE = "aether.enhancedLocalRepository.splitRemote";
162
163    private static final String R1_CONF_PROP_SPLIT_REMOTE_REPOSITORY =
164            "aether.enhancedLocalRepository.splitRemoteRepository";
165
166    private static final String R1_CONF_PROP_SPLIT_REMOTE_REPOSITORY_LAST =
167            "aether.enhancedLocalRepository.splitRemoteRepositoryLast";
168
169    private static final String R1_CONF_PROP_RELEASES_PREFIX = "aether.enhancedLocalRepository.releasesPrefix";
170
171    private static final String R1_CONF_PROP_SNAPSHOTS_PREFIX = "aether.enhancedLocalRepository.snapshotsPrefix";
172
173    protected boolean isSplit(RepositorySystemSession session) {
174        return ConfigUtils.getBoolean(session, DEFAULT_SPLIT, CONFIG_PROP_SPLIT, R1_CONF_PROP_SPLIT);
175    }
176
177    protected String getLocalPrefix(RepositorySystemSession session) {
178        return ConfigUtils.getString(
179                session, DEFAULT_LOCAL_PREFIX, CONFIG_PROP_LOCAL_PREFIX, R1_CONF_PROP_LOCAL_PREFIX);
180    }
181
182    protected boolean isSplitLocal(RepositorySystemSession session) {
183        return ConfigUtils.getBoolean(session, DEFAULT_SPLIT_LOCAL, CONFIG_PROP_SPLIT_LOCAL, R1_CONF_PROP_SPLIT_LOCAL);
184    }
185
186    protected String getRemotePrefix(RepositorySystemSession session) {
187        return ConfigUtils.getString(
188                session, DEFAULT_REMOTE_PREFIX, CONFIG_PROP_REMOTE_PREFIX, R1_CONF_PROP_REMOTE_PREFIX);
189    }
190
191    protected boolean isSplitRemote(RepositorySystemSession session) {
192        return ConfigUtils.getBoolean(
193                session, DEFAULT_SPLIT_REMOTE, CONFIG_PROP_SPLIT_REMOTE, R1_CONF_PROP_SPLIT_REMOTE);
194    }
195
196    protected boolean isSplitRemoteRepository(RepositorySystemSession session) {
197        return ConfigUtils.getBoolean(
198                session,
199                DEFAULT_SPLIT_REMOTE_REPOSITORY,
200                CONFIG_PROP_SPLIT_REMOTE_REPOSITORY,
201                R1_CONF_PROP_SPLIT_REMOTE_REPOSITORY);
202    }
203
204    protected boolean isSplitRemoteRepositoryLast(RepositorySystemSession session) {
205        return ConfigUtils.getBoolean(
206                session,
207                DEFAULT_SPLIT_REMOTE_REPOSITORY_LAST,
208                CONFIG_PROP_SPLIT_REMOTE_REPOSITORY_LAST,
209                R1_CONF_PROP_SPLIT_REMOTE_REPOSITORY_LAST);
210    }
211
212    protected String getReleasesPrefix(RepositorySystemSession session) {
213        return ConfigUtils.getString(
214                session, DEFAULT_RELEASES_PREFIX, CONFIG_PROP_RELEASES_PREFIX, R1_CONF_PROP_RELEASES_PREFIX);
215    }
216
217    protected String getSnapshotsPrefix(RepositorySystemSession session) {
218        return ConfigUtils.getString(
219                session, DEFAULT_SNAPSHOTS_PREFIX, CONFIG_PROP_SNAPSHOTS_PREFIX, R1_CONF_PROP_SNAPSHOTS_PREFIX);
220    }
221
222    /**
223     * Support class for composers: it defines protected members for all the predefined configuration values and
224     * provides default implementation for methods. Implementors may change it's behaviour by overriding methods.
225     */
226    @SuppressWarnings("checkstyle:parameternumber")
227    protected abstract static class LocalPathPrefixComposerSupport implements LocalPathPrefixComposer {
228        protected final boolean split;
229
230        protected final String localPrefix;
231
232        protected final boolean splitLocal;
233
234        protected final String remotePrefix;
235
236        protected final boolean splitRemote;
237
238        protected final boolean splitRemoteRepository;
239
240        protected final boolean splitRemoteRepositoryLast;
241
242        protected final String releasesPrefix;
243
244        protected final String snapshotsPrefix;
245
246        protected final RepositoryKeyFunction repositoryKeyFunction;
247
248        protected LocalPathPrefixComposerSupport(
249                boolean split,
250                String localPrefix,
251                boolean splitLocal,
252                String remotePrefix,
253                boolean splitRemote,
254                boolean splitRemoteRepository,
255                boolean splitRemoteRepositoryLast,
256                String releasesPrefix,
257                String snapshotsPrefix,
258                RepositoryKeyFunction repositoryKeyFunction) {
259            this.split = split;
260            this.localPrefix = localPrefix;
261            this.splitLocal = splitLocal;
262            this.remotePrefix = remotePrefix;
263            this.splitRemote = splitRemote;
264            this.splitRemoteRepository = splitRemoteRepository;
265            this.splitRemoteRepositoryLast = splitRemoteRepositoryLast;
266            this.releasesPrefix = releasesPrefix;
267            this.snapshotsPrefix = snapshotsPrefix;
268            this.repositoryKeyFunction = repositoryKeyFunction;
269        }
270
271        @Override
272        public String getPathPrefixForLocalArtifact(Artifact artifact) {
273            if (!split) {
274                return null;
275            }
276            String result = localPrefix;
277            if (splitLocal) {
278                result += "/" + (artifact.isSnapshot() ? snapshotsPrefix : releasesPrefix);
279            }
280            return result;
281        }
282
283        @Override
284        public String getPathPrefixForRemoteArtifact(Artifact artifact, RemoteRepository repository) {
285            if (!split) {
286                return null;
287            }
288            String result = remotePrefix;
289            if (!splitRemoteRepositoryLast && splitRemoteRepository) {
290                result += "/" + repositoryKeySegment(repository);
291            }
292            if (splitRemote) {
293                result += "/" + (artifact.isSnapshot() ? snapshotsPrefix : releasesPrefix);
294            }
295            if (splitRemoteRepositoryLast && splitRemoteRepository) {
296                result += "/" + repositoryKeySegment(repository);
297            }
298            return result;
299        }
300
301        @Override
302        public String getPathPrefixForLocalMetadata(Metadata metadata) {
303            if (!split) {
304                return null;
305            }
306            String result = localPrefix;
307            if (splitLocal) {
308                result += "/" + (isSnapshot(metadata) ? snapshotsPrefix : releasesPrefix);
309            }
310            return result;
311        }
312
313        @Override
314        public String getPathPrefixForRemoteMetadata(Metadata metadata, RemoteRepository repository) {
315            if (!split) {
316                return null;
317            }
318            String result = remotePrefix;
319            if (!splitRemoteRepositoryLast && splitRemoteRepository) {
320                result += "/" + repositoryKeySegment(repository);
321            }
322            if (splitRemote) {
323                result += "/" + (isSnapshot(metadata) ? snapshotsPrefix : releasesPrefix);
324            }
325            if (splitRemoteRepositoryLast && splitRemoteRepository) {
326                result += "/" + repositoryKeySegment(repository);
327            }
328            return result;
329        }
330
331        protected boolean isSnapshot(Metadata metadata) {
332            return !metadata.getVersion().isEmpty() && metadata.getVersion().endsWith("-SNAPSHOT");
333        }
334
335        /**
336         * Defense in depth: the repository key is spliced into the local repository path prefix, so it
337         * must be a safe path segment.
338         */
339        private String repositoryKeySegment(RemoteRepository repository) {
340            String key = repositoryKeyFunction.apply(repository, null);
341            PathUtils.validatePathComponent(key, "repository key");
342            return key;
343        }
344    }
345}