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;
020
021import java.util.Collections;
022import java.util.HashMap;
023import java.util.Map;
024import java.util.function.Function;
025
026import org.eclipse.aether.artifact.ArtifactType;
027import org.eclipse.aether.artifact.ArtifactTypeRegistry;
028import org.eclipse.aether.collection.DependencyGraphTransformer;
029import org.eclipse.aether.collection.DependencyManager;
030import org.eclipse.aether.collection.DependencySelector;
031import org.eclipse.aether.collection.DependencyTraverser;
032import org.eclipse.aether.collection.VersionFilter;
033import org.eclipse.aether.repository.Authentication;
034import org.eclipse.aether.repository.AuthenticationSelector;
035import org.eclipse.aether.repository.LocalRepository;
036import org.eclipse.aether.repository.LocalRepositoryManager;
037import org.eclipse.aether.repository.MirrorSelector;
038import org.eclipse.aether.repository.Proxy;
039import org.eclipse.aether.repository.ProxySelector;
040import org.eclipse.aether.repository.RemoteRepository;
041import org.eclipse.aether.repository.RepositoryPolicy;
042import org.eclipse.aether.repository.WorkspaceReader;
043import org.eclipse.aether.resolution.ArtifactDescriptorPolicy;
044import org.eclipse.aether.resolution.ResolutionErrorPolicy;
045import org.eclipse.aether.transfer.TransferListener;
046
047import static java.util.Objects.requireNonNull;
048
049/**
050 * A simple repository system session.
051 * <p>
052 * <strong>Note:</strong> This class is not thread-safe. It is assumed that the mutators get only called during an
053 * initialization phase and that the session itself is not changed once initialized and being used by the repository
054 * system. It is recommended to call {@link #setReadOnly()} once the session has been fully initialized to prevent
055 * accidental manipulation of it afterward.
056 */
057public final class DefaultRepositorySystemSession implements RepositorySystemSession {
058    private boolean readOnly;
059
060    private boolean offline;
061
062    private boolean ignoreArtifactDescriptorRepositories;
063
064    private ResolutionErrorPolicy resolutionErrorPolicy;
065
066    private ArtifactDescriptorPolicy artifactDescriptorPolicy;
067
068    private String checksumPolicy;
069
070    private String artifactUpdatePolicy;
071
072    private String metadataUpdatePolicy;
073
074    private LocalRepositoryManager localRepositoryManager;
075
076    private WorkspaceReader workspaceReader;
077
078    private RepositoryListener repositoryListener;
079
080    private TransferListener transferListener;
081
082    private Map<String, String> systemProperties;
083
084    private Map<String, String> systemPropertiesView;
085
086    private Map<String, String> userProperties;
087
088    private Map<String, String> userPropertiesView;
089
090    private Map<String, Object> configProperties;
091
092    private Map<String, Object> configPropertiesView;
093
094    private MirrorSelector mirrorSelector;
095
096    private ProxySelector proxySelector;
097
098    private AuthenticationSelector authenticationSelector;
099
100    private ArtifactTypeRegistry artifactTypeRegistry;
101
102    private DependencyTraverser dependencyTraverser;
103
104    private DependencyManager dependencyManager;
105
106    private DependencySelector dependencySelector;
107
108    private VersionFilter versionFilter;
109
110    private DependencyGraphTransformer dependencyGraphTransformer;
111
112    private SessionData data;
113
114    private RepositoryCache cache;
115
116    private final Function<Runnable, Boolean> onSessionEndedRegistrar;
117
118    /**
119     * Creates an uninitialized session. <em>Note:</em> The new session is not ready to use, as a bare minimum,
120     * {@link #setLocalRepositoryManager(LocalRepositoryManager)} needs to be called but usually other settings also
121     * need to be customized to achieve meaningful behavior.
122     *
123     * @deprecated This way of creating session should be avoided, is in place just to offer backward binary
124     * compatibility with Resolver 1.x using code, but offers reduced functionality.
125     * Use {@link RepositorySystem#createSessionBuilder()} instead.
126     */
127    @Deprecated
128    public DefaultRepositorySystemSession() {
129        systemProperties = new HashMap<>();
130        systemPropertiesView = Collections.unmodifiableMap(systemProperties);
131        userProperties = new HashMap<>();
132        userPropertiesView = Collections.unmodifiableMap(userProperties);
133        configProperties = new HashMap<>();
134        configPropertiesView = Collections.unmodifiableMap(configProperties);
135        mirrorSelector = NullMirrorSelector.INSTANCE;
136        proxySelector = NullProxySelector.INSTANCE;
137        authenticationSelector = NullAuthenticationSelector.INSTANCE;
138        artifactTypeRegistry = NullArtifactTypeRegistry.INSTANCE;
139        data = new DefaultSessionData();
140        onSessionEndedRegistrar = h -> false;
141    }
142
143    /**
144     * Creates a shallow copy of the specified session. Actually, the copy is not completely shallow, all maps holding
145     * system/user/config properties are copied as well. In other words, invoking any mutator on the new session itself
146     * has no effect on the original session. Other mutable objects like the session data and cache (if any) are not
147     * copied and will be shared with the original session unless reconfigured.
148     *
149     * @param session The session to copy, must not be {@code null}.
150     */
151    public DefaultRepositorySystemSession(RepositorySystemSession session) {
152        requireNonNull(session, "repository system session cannot be null");
153
154        setOffline(session.isOffline());
155        setIgnoreArtifactDescriptorRepositories(session.isIgnoreArtifactDescriptorRepositories());
156        setResolutionErrorPolicy(session.getResolutionErrorPolicy());
157        setArtifactDescriptorPolicy(session.getArtifactDescriptorPolicy());
158        setChecksumPolicy(session.getChecksumPolicy());
159        setUpdatePolicy(session.getUpdatePolicy());
160        setMetadataUpdatePolicy(session.getMetadataUpdatePolicy());
161        setLocalRepositoryManager(session.getLocalRepositoryManager());
162        setWorkspaceReader(session.getWorkspaceReader());
163        setRepositoryListener(session.getRepositoryListener());
164        setTransferListener(session.getTransferListener());
165        setSystemProperties(session.getSystemProperties());
166        setUserProperties(session.getUserProperties());
167        setConfigProperties(session.getConfigProperties());
168        setMirrorSelector(session.getMirrorSelector());
169        setProxySelector(session.getProxySelector());
170        setAuthenticationSelector(session.getAuthenticationSelector());
171        setArtifactTypeRegistry(session.getArtifactTypeRegistry());
172        setDependencyTraverser(session.getDependencyTraverser());
173        setDependencyManager(session.getDependencyManager());
174        setDependencySelector(session.getDependencySelector());
175        setVersionFilter(session.getVersionFilter());
176        setDependencyGraphTransformer(session.getDependencyGraphTransformer());
177        setData(session.getData());
178        setCache(session.getCache());
179        this.onSessionEndedRegistrar = session::addOnSessionEndedHandler;
180    }
181
182    @Override
183    public boolean isOffline() {
184        return offline;
185    }
186
187    /**
188     * Controls whether the repository system operates in offline mode and avoids/refuses any access to remote
189     * repositories.
190     *
191     * @param offline {@code true} if the repository system is in offline mode, {@code false} otherwise.
192     * @return This session for chaining, never {@code null}.
193     */
194    public DefaultRepositorySystemSession setOffline(boolean offline) {
195        verifyStateForMutation();
196        this.offline = offline;
197        return this;
198    }
199
200    @Override
201    public boolean isIgnoreArtifactDescriptorRepositories() {
202        return ignoreArtifactDescriptorRepositories;
203    }
204
205    /**
206     * Controls whether repositories declared in artifact descriptors should be ignored during transitive dependency
207     * collection. If enabled, only the repositories originally provided with the collect request will be considered.
208     *
209     * @param ignoreArtifactDescriptorRepositories {@code true} to ignore additional repositories from artifact
210     *                                             descriptors, {@code false} to merge those with the originally
211     *                                             specified repositories.
212     * @return This session for chaining, never {@code null}.
213     */
214    public DefaultRepositorySystemSession setIgnoreArtifactDescriptorRepositories(
215            boolean ignoreArtifactDescriptorRepositories) {
216        verifyStateForMutation();
217        this.ignoreArtifactDescriptorRepositories = ignoreArtifactDescriptorRepositories;
218        return this;
219    }
220
221    @Override
222    public ResolutionErrorPolicy getResolutionErrorPolicy() {
223        return resolutionErrorPolicy;
224    }
225
226    /**
227     * Sets the policy which controls whether resolutions errors from remote repositories should be cached.
228     *
229     * @param resolutionErrorPolicy The resolution error policy for this session, may be {@code null} if resolution
230     *                              errors should generally not be cached.
231     * @return This session for chaining, never {@code null}.
232     */
233    public DefaultRepositorySystemSession setResolutionErrorPolicy(ResolutionErrorPolicy resolutionErrorPolicy) {
234        verifyStateForMutation();
235        this.resolutionErrorPolicy = resolutionErrorPolicy;
236        return this;
237    }
238
239    @Override
240    public ArtifactDescriptorPolicy getArtifactDescriptorPolicy() {
241        return artifactDescriptorPolicy;
242    }
243
244    /**
245     * Sets the policy which controls how errors related to reading artifact descriptors should be handled.
246     *
247     * @param artifactDescriptorPolicy The descriptor error policy for this session, may be {@code null} if descriptor
248     *                                 errors should generally not be tolerated.
249     * @return This session for chaining, never {@code null}.
250     */
251    public DefaultRepositorySystemSession setArtifactDescriptorPolicy(
252            ArtifactDescriptorPolicy artifactDescriptorPolicy) {
253        verifyStateForMutation();
254        this.artifactDescriptorPolicy = artifactDescriptorPolicy;
255        return this;
256    }
257
258    @Override
259    public String getChecksumPolicy() {
260        return checksumPolicy;
261    }
262
263    /**
264     * Sets the global checksum policy. If set, the global checksum policy overrides the checksum policies of the remote
265     * repositories being used for resolution.
266     *
267     * @param checksumPolicy The global checksum policy, may be {@code null}/empty to apply the per-repository policies.
268     * @return This session for chaining, never {@code null}.
269     * @see RepositoryPolicy#CHECKSUM_POLICY_FAIL
270     * @see RepositoryPolicy#CHECKSUM_POLICY_IGNORE
271     * @see RepositoryPolicy#CHECKSUM_POLICY_WARN
272     */
273    public DefaultRepositorySystemSession setChecksumPolicy(String checksumPolicy) {
274        verifyStateForMutation();
275        this.checksumPolicy = checksumPolicy;
276        return this;
277    }
278
279    @Override
280    public String getUpdatePolicy() {
281        return getArtifactUpdatePolicy();
282    }
283
284    /**
285     * Sets the global update policy. If set, the global update policy overrides the update policies of the remote
286     * repositories being used for resolution.
287     * <p>
288     * This method is meant for code that does not want to distinguish between artifact and metadata policies.
289     * Note: applications should either use get/set updatePolicy (this method and
290     * {@link RepositorySystemSession#getUpdatePolicy()}) or also distinguish between artifact and
291     * metadata update policies (and use other methods), but <em>should not mix the two!</em>
292     *
293     * @param updatePolicy The global update policy, may be {@code null}/empty to apply the per-repository policies.
294     * @return This session for chaining, never {@code null}.
295     * @see RepositoryPolicy#UPDATE_POLICY_ALWAYS
296     * @see RepositoryPolicy#UPDATE_POLICY_DAILY
297     * @see RepositoryPolicy#UPDATE_POLICY_NEVER
298     * @see #setArtifactUpdatePolicy(String)
299     * @see #setMetadataUpdatePolicy(String)
300     */
301    public DefaultRepositorySystemSession setUpdatePolicy(String updatePolicy) {
302        verifyStateForMutation();
303        setArtifactUpdatePolicy(updatePolicy);
304        setMetadataUpdatePolicy(updatePolicy);
305        return this;
306    }
307
308    @Override
309    public String getArtifactUpdatePolicy() {
310        return artifactUpdatePolicy;
311    }
312
313    /**
314     * Sets the global artifact update policy. If set, the global update policy overrides the artifact update policies
315     * of the remote repositories being used for resolution.
316     *
317     * @param artifactUpdatePolicy The global update policy, may be {@code null}/empty to apply the per-repository policies.
318     * @return This session for chaining, never {@code null}.
319     * @see RepositoryPolicy#UPDATE_POLICY_ALWAYS
320     * @see RepositoryPolicy#UPDATE_POLICY_DAILY
321     * @see RepositoryPolicy#UPDATE_POLICY_NEVER
322     * @since 2.0.0
323     */
324    public DefaultRepositorySystemSession setArtifactUpdatePolicy(String artifactUpdatePolicy) {
325        verifyStateForMutation();
326        this.artifactUpdatePolicy = artifactUpdatePolicy;
327        return this;
328    }
329
330    @Override
331    public String getMetadataUpdatePolicy() {
332        return metadataUpdatePolicy;
333    }
334
335    /**
336     * Sets the global metadata update policy. If set, the global update policy overrides the metadata update policies
337     * of the remote repositories being used for resolution.
338     *
339     * @param metadataUpdatePolicy The global update policy, may be {@code null}/empty to apply the per-repository policies.
340     * @return This session for chaining, never {@code null}.
341     * @see RepositoryPolicy#UPDATE_POLICY_ALWAYS
342     * @see RepositoryPolicy#UPDATE_POLICY_DAILY
343     * @see RepositoryPolicy#UPDATE_POLICY_NEVER
344     * @since 2.0.0
345     */
346    public DefaultRepositorySystemSession setMetadataUpdatePolicy(String metadataUpdatePolicy) {
347        verifyStateForMutation();
348        this.metadataUpdatePolicy = metadataUpdatePolicy;
349        return this;
350    }
351
352    @Override
353    public LocalRepository getLocalRepository() {
354        LocalRepositoryManager lrm = getLocalRepositoryManager();
355        return (lrm != null) ? lrm.getRepository() : null;
356    }
357
358    public LocalRepositoryManager getLocalRepositoryManager() {
359        return localRepositoryManager;
360    }
361
362    /**
363     * Sets the local repository manager used during this session. <em>Note:</em> Eventually, a valid session must have
364     * a local repository manager set.
365     *
366     * @param localRepositoryManager The local repository manager used during this session, may be {@code null}.
367     * @return This session for chaining, never {@code null}.
368     */
369    public DefaultRepositorySystemSession setLocalRepositoryManager(LocalRepositoryManager localRepositoryManager) {
370        verifyStateForMutation();
371        this.localRepositoryManager = localRepositoryManager;
372        return this;
373    }
374
375    @Override
376    public WorkspaceReader getWorkspaceReader() {
377        return workspaceReader;
378    }
379
380    /**
381     * Sets the workspace reader used during this session. If set, the workspace reader will usually be consulted first
382     * to resolve artifacts.
383     *
384     * @param workspaceReader The workspace reader for this session, may be {@code null} if none.
385     * @return This session for chaining, never {@code null}.
386     */
387    public DefaultRepositorySystemSession setWorkspaceReader(WorkspaceReader workspaceReader) {
388        verifyStateForMutation();
389        this.workspaceReader = workspaceReader;
390        return this;
391    }
392
393    @Override
394    public RepositoryListener getRepositoryListener() {
395        return repositoryListener;
396    }
397
398    /**
399     * Sets the listener being notified of actions in the repository system.
400     *
401     * @param repositoryListener The repository listener, may be {@code null} if none.
402     * @return This session for chaining, never {@code null}.
403     */
404    public DefaultRepositorySystemSession setRepositoryListener(RepositoryListener repositoryListener) {
405        verifyStateForMutation();
406        this.repositoryListener = repositoryListener;
407        return this;
408    }
409
410    @Override
411    public TransferListener getTransferListener() {
412        return transferListener;
413    }
414
415    /**
416     * Sets the listener being notified of uploads/downloads by the repository system.
417     *
418     * @param transferListener The transfer listener, may be {@code null} if none.
419     * @return This session for chaining, never {@code null}.
420     */
421    public DefaultRepositorySystemSession setTransferListener(TransferListener transferListener) {
422        verifyStateForMutation();
423        this.transferListener = transferListener;
424        return this;
425    }
426
427    @SuppressWarnings("checkstyle:magicnumber")
428    private <T> Map<String, T> copySafe(Map<?, ?> table, Class<T> valueType) {
429        Map<String, T> map;
430        if (table == null || table.isEmpty()) {
431            map = new HashMap<>();
432        } else {
433            map = new HashMap<>((int) (table.size() / 0.75f) + 1);
434            for (Map.Entry<?, ?> entry : table.entrySet()) {
435                Object key = entry.getKey();
436                if (key instanceof String) {
437                    Object value = entry.getValue();
438                    if (valueType.isInstance(value)) {
439                        map.put(key.toString(), valueType.cast(value));
440                    }
441                }
442            }
443        }
444        return map;
445    }
446
447    @Override
448    public Map<String, String> getSystemProperties() {
449        return systemPropertiesView;
450    }
451
452    /**
453     * Sets the system properties to use, e.g. for processing of artifact descriptors. System properties are usually
454     * collected from the runtime environment like {@link System#getProperties()} and environment variables.
455     * <p>
456     * <em>Note:</em> System properties are of type {@code Map<String, String>} and any key-value pair in the input map
457     * that doesn't match this type will be silently ignored.
458     *
459     * @param systemProperties The system properties, may be {@code null} or empty if none.
460     * @return This session for chaining, never {@code null}.
461     */
462    public DefaultRepositorySystemSession setSystemProperties(Map<?, ?> systemProperties) {
463        verifyStateForMutation();
464        this.systemProperties = copySafe(systemProperties, String.class);
465        systemPropertiesView = Collections.unmodifiableMap(this.systemProperties);
466        return this;
467    }
468
469    /**
470     * Sets the specified system property.
471     *
472     * @param key   The property key, must not be {@code null}.
473     * @param value The property value, may be {@code null} to remove/unset the property.
474     * @return This session for chaining, never {@code null}.
475     */
476    public DefaultRepositorySystemSession setSystemProperty(String key, String value) {
477        verifyStateForMutation();
478        if (value != null) {
479            systemProperties.put(key, value);
480        } else {
481            systemProperties.remove(key);
482        }
483        return this;
484    }
485
486    @Override
487    public Map<String, String> getUserProperties() {
488        return userPropertiesView;
489    }
490
491    /**
492     * Sets the user properties to use, e.g. for processing of artifact descriptors. User properties are similar to
493     * system properties but are set on the discretion of the user and hence are considered of higher priority than
494     * system properties in case of conflicts.
495     * <p>
496     * <em>Note:</em> User properties are of type {@code Map<String, String>} and any key-value pair in the input map
497     * that doesn't match this type will be silently ignored.
498     *
499     * @param userProperties The user properties, may be {@code null} or empty if none.
500     * @return This session for chaining, never {@code null}.
501     */
502    public DefaultRepositorySystemSession setUserProperties(Map<?, ?> userProperties) {
503        verifyStateForMutation();
504        this.userProperties = copySafe(userProperties, String.class);
505        userPropertiesView = Collections.unmodifiableMap(this.userProperties);
506        return this;
507    }
508
509    /**
510     * Sets the specified user property.
511     *
512     * @param key   The property key, must not be {@code null}.
513     * @param value The property value, may be {@code null} to remove/unset the property.
514     * @return This session for chaining, never {@code null}.
515     */
516    public DefaultRepositorySystemSession setUserProperty(String key, String value) {
517        verifyStateForMutation();
518        if (value != null) {
519            userProperties.put(key, value);
520        } else {
521            userProperties.remove(key);
522        }
523        return this;
524    }
525
526    @Override
527    public Map<String, Object> getConfigProperties() {
528        return configPropertiesView;
529    }
530
531    /**
532     * Sets the configuration properties used to tweak internal aspects of the repository system (e.g. thread pooling,
533     * connector-specific behavior, etc.).
534     * <p>
535     * <em>Note:</em> Configuration properties are of type {@code Map<String, Object>} and any key-value pair in the
536     * input map that doesn't match this type will be silently ignored.
537     *
538     * @param configProperties The configuration properties, may be {@code null} or empty if none.
539     * @return This session for chaining, never {@code null}.
540     */
541    public DefaultRepositorySystemSession setConfigProperties(Map<?, ?> configProperties) {
542        verifyStateForMutation();
543        this.configProperties = copySafe(configProperties, Object.class);
544        configPropertiesView = Collections.unmodifiableMap(this.configProperties);
545        return this;
546    }
547
548    /**
549     * Sets the specified configuration property.
550     *
551     * @param key   The property key, must not be {@code null}.
552     * @param value The property value, may be {@code null} to remove/unset the property.
553     * @return This session for chaining, never {@code null}.
554     */
555    public DefaultRepositorySystemSession setConfigProperty(String key, Object value) {
556        verifyStateForMutation();
557        if (value != null) {
558            configProperties.put(key, value);
559        } else {
560            configProperties.remove(key);
561        }
562        return this;
563    }
564
565    @Override
566    public MirrorSelector getMirrorSelector() {
567        return mirrorSelector;
568    }
569
570    /**
571     * Sets the mirror selector to use for repositories discovered in artifact descriptors. Note that this selector is
572     * not used for remote repositories which are passed as request parameters to the repository system, those
573     * repositories are supposed to denote the effective repositories.
574     *
575     * @param mirrorSelector The mirror selector to use, may be {@code null}.
576     * @return This session for chaining, never {@code null}.
577     */
578    public DefaultRepositorySystemSession setMirrorSelector(MirrorSelector mirrorSelector) {
579        verifyStateForMutation();
580        this.mirrorSelector = mirrorSelector;
581        if (this.mirrorSelector == null) {
582            this.mirrorSelector = NullMirrorSelector.INSTANCE;
583        }
584        return this;
585    }
586
587    @Override
588    public ProxySelector getProxySelector() {
589        return proxySelector;
590    }
591
592    /**
593     * Sets the proxy selector to use for repositories discovered in artifact descriptors. Note that this selector is
594     * not used for remote repositories which are passed as request parameters to the repository system, those
595     * repositories are supposed to have their proxy (if any) already set.
596     *
597     * @param proxySelector The proxy selector to use, may be {@code null}.
598     * @return This session for chaining, never {@code null}.
599     * @see org.eclipse.aether.repository.RemoteRepository#getProxy()
600     */
601    public DefaultRepositorySystemSession setProxySelector(ProxySelector proxySelector) {
602        verifyStateForMutation();
603        this.proxySelector = proxySelector;
604        if (this.proxySelector == null) {
605            this.proxySelector = NullProxySelector.INSTANCE;
606        }
607        return this;
608    }
609
610    @Override
611    public AuthenticationSelector getAuthenticationSelector() {
612        return authenticationSelector;
613    }
614
615    /**
616     * Sets the authentication selector to use for repositories discovered in artifact descriptors. Note that this
617     * selector is not used for remote repositories which are passed as request parameters to the repository system,
618     * those repositories are supposed to have their authentication (if any) already set.
619     *
620     * @param authenticationSelector The authentication selector to use, may be {@code null}.
621     * @return This session for chaining, never {@code null}.
622     * @see org.eclipse.aether.repository.RemoteRepository#getAuthentication()
623     */
624    public DefaultRepositorySystemSession setAuthenticationSelector(AuthenticationSelector authenticationSelector) {
625        verifyStateForMutation();
626        this.authenticationSelector = authenticationSelector;
627        if (this.authenticationSelector == null) {
628            this.authenticationSelector = NullAuthenticationSelector.INSTANCE;
629        }
630        return this;
631    }
632
633    @Override
634    public ArtifactTypeRegistry getArtifactTypeRegistry() {
635        return artifactTypeRegistry;
636    }
637
638    /**
639     * Sets the registry of artifact types recognized by this session.
640     *
641     * @param artifactTypeRegistry The artifact type registry, may be {@code null}.
642     * @return This session for chaining, never {@code null}.
643     */
644    public DefaultRepositorySystemSession setArtifactTypeRegistry(ArtifactTypeRegistry artifactTypeRegistry) {
645        verifyStateForMutation();
646        this.artifactTypeRegistry = artifactTypeRegistry;
647        if (this.artifactTypeRegistry == null) {
648            this.artifactTypeRegistry = NullArtifactTypeRegistry.INSTANCE;
649        }
650        return this;
651    }
652
653    @Override
654    public DependencyTraverser getDependencyTraverser() {
655        return dependencyTraverser;
656    }
657
658    /**
659     * Sets the dependency traverser to use for building dependency graphs.
660     *
661     * @param dependencyTraverser The dependency traverser to use for building dependency graphs, may be {@code null}.
662     * @return This session for chaining, never {@code null}.
663     */
664    public DefaultRepositorySystemSession setDependencyTraverser(DependencyTraverser dependencyTraverser) {
665        verifyStateForMutation();
666        this.dependencyTraverser = dependencyTraverser;
667        return this;
668    }
669
670    @Override
671    public DependencyManager getDependencyManager() {
672        return dependencyManager;
673    }
674
675    /**
676     * Sets the dependency manager to use for building dependency graphs.
677     *
678     * @param dependencyManager The dependency manager to use for building dependency graphs, may be {@code null}.
679     * @return This session for chaining, never {@code null}.
680     */
681    public DefaultRepositorySystemSession setDependencyManager(DependencyManager dependencyManager) {
682        verifyStateForMutation();
683        this.dependencyManager = dependencyManager;
684        return this;
685    }
686
687    @Override
688    public DependencySelector getDependencySelector() {
689        return dependencySelector;
690    }
691
692    /**
693     * Sets the dependency selector to use for building dependency graphs.
694     *
695     * @param dependencySelector The dependency selector to use for building dependency graphs, may be {@code null}.
696     * @return This session for chaining, never {@code null}.
697     */
698    public DefaultRepositorySystemSession setDependencySelector(DependencySelector dependencySelector) {
699        verifyStateForMutation();
700        this.dependencySelector = dependencySelector;
701        return this;
702    }
703
704    @Override
705    public VersionFilter getVersionFilter() {
706        return versionFilter;
707    }
708
709    /**
710     * Sets the version filter to use for building dependency graphs.
711     *
712     * @param versionFilter The version filter to use for building dependency graphs, may be {@code null} to not filter
713     *                      versions.
714     * @return This session for chaining, never {@code null}.
715     */
716    public DefaultRepositorySystemSession setVersionFilter(VersionFilter versionFilter) {
717        verifyStateForMutation();
718        this.versionFilter = versionFilter;
719        return this;
720    }
721
722    @Override
723    public DependencyGraphTransformer getDependencyGraphTransformer() {
724        return dependencyGraphTransformer;
725    }
726
727    /**
728     * Sets the dependency graph transformer to use for building dependency graphs.
729     *
730     * @param dependencyGraphTransformer The dependency graph transformer to use for building dependency graphs, may be
731     *                                   {@code null}.
732     * @return This session for chaining, never {@code null}.
733     */
734    public DefaultRepositorySystemSession setDependencyGraphTransformer(
735            DependencyGraphTransformer dependencyGraphTransformer) {
736        verifyStateForMutation();
737        this.dependencyGraphTransformer = dependencyGraphTransformer;
738        return this;
739    }
740
741    @Override
742    public SessionData getData() {
743        return data;
744    }
745
746    /**
747     * Sets the custom data associated with this session.
748     *
749     * @param data The session data, may be {@code null}.
750     * @return This session for chaining, never {@code null}.
751     */
752    public DefaultRepositorySystemSession setData(SessionData data) {
753        verifyStateForMutation();
754        this.data = data;
755        if (this.data == null) {
756            this.data = new DefaultSessionData();
757        }
758        return this;
759    }
760
761    @Override
762    public RepositoryCache getCache() {
763        return cache;
764    }
765
766    /**
767     * Sets the cache the repository system may use to save data for future reuse during the session.
768     *
769     * @param cache The repository cache, may be {@code null} if none.
770     * @return This session for chaining, never {@code null}.
771     */
772    public DefaultRepositorySystemSession setCache(RepositoryCache cache) {
773        verifyStateForMutation();
774        this.cache = cache;
775        return this;
776    }
777
778    /**
779     * Registers onSessionEnded handler, if able to.
780     *
781     * @param handler The handler to register
782     * @return Return {@code true} if registration was possible, otherwise {@code false}.
783     */
784    @Override
785    public boolean addOnSessionEndedHandler(Runnable handler) {
786        return onSessionEndedRegistrar.apply(handler);
787    }
788
789    /**
790     * Marks this session as read-only such that any future attempts to call its mutators will fail with an exception.
791     * Marking an already read-only session as read-only has no effect. The session's data and cache remain writable
792     * though.
793     */
794    public void setReadOnly() {
795        readOnly = true;
796    }
797
798    /**
799     * Verifies this instance state for mutation operations: mutated instance must not be read-only or closed.
800     */
801    private void verifyStateForMutation() {
802        if (readOnly) {
803            throw new IllegalStateException("repository system session is read-only");
804        }
805    }
806
807    static class NullProxySelector implements ProxySelector {
808
809        public static final ProxySelector INSTANCE = new NullProxySelector();
810
811        public Proxy getProxy(RemoteRepository repository) {
812            requireNonNull(repository, "repository cannot be null");
813            return repository.getProxy();
814        }
815    }
816
817    static class NullMirrorSelector implements MirrorSelector {
818
819        public static final MirrorSelector INSTANCE = new NullMirrorSelector();
820
821        public RemoteRepository getMirror(RemoteRepository repository) {
822            requireNonNull(repository, "repository cannot be null");
823            return null;
824        }
825    }
826
827    static class NullAuthenticationSelector implements AuthenticationSelector {
828
829        public static final AuthenticationSelector INSTANCE = new NullAuthenticationSelector();
830
831        public Authentication getAuthentication(RemoteRepository repository) {
832            requireNonNull(repository, "repository cannot be null");
833            return repository.getAuthentication();
834        }
835    }
836
837    static final class NullArtifactTypeRegistry implements ArtifactTypeRegistry {
838
839        public static final ArtifactTypeRegistry INSTANCE = new NullArtifactTypeRegistry();
840
841        public ArtifactType get(String typeId) {
842            return null;
843        }
844    }
845}