[MNG-7914] Provide a single entry point for configuration (#1595)

* [MNG-7914] Provide a single entry point for configuration
* [MNG-7914] Rename global -> installation
* [MNG-7914] Include time zone in Maven build timestamp
* [MNG-7914] Use a single place to document all maven properties
This commit is contained in:
Guillaume Nodet 2024-08-22 16:47:43 +02:00 committed by GitHub
parent 6668da3219
commit 6ac914d6f9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
40 changed files with 3504 additions and 275 deletions

View File

@ -1,6 +1,7 @@
main is org.apache.maven.cli.MavenCli from plexus.core
set maven.conf default ${maven.home}/conf
set maven.installation.conf default ${maven.conf}
[plexus.core]
load ${maven.conf}/logging

View File

@ -0,0 +1,40 @@
#
# Maven user properties
#
maven.installation.conf = ${maven.home}/conf
maven.user.conf = ${user.home}/.m2
maven.project.conf = ${session.rootDirectory}/.mvn
# Comma-separated list of files to include.
# Each item may be enclosed in quotes to gracefully include spaces. Items are trimmed before being loaded.
# If the first character of an item is a question mark, the load will silently fail if the file does not exist.
${includes} = ?"${maven.user.conf}/maven.properties", \
?"${maven.project.conf}/maven.properties"
#
# Settings
#
# Define the default three levels for settings.
# The '-is' flag will override the 'maven.installation.settings' property.
# The '-ps' flag will override the 'maven.project.settings' property.
# The '-s' flag will override the 'maven.user.settings' property.
maven.installation.settings = ${maven.installation.conf}/settings.xml
maven.project.settings = ${maven.project.conf}/settings.xml
maven.user.settings = ${maven.user.conf}/settings.xml
#
# Toolchains
#
# Define the default three levels for toolchains.
# The '-it' flag will override the 'maven.installation.toolchains' property.
# The '-t' flag will override the 'maven.user.toolchains' property.
maven.installation.toolchains = ${maven.installation.conf}/toolchains.xml
maven.user.toolchains = ${maven.user.conf}/toolchains.xml
#
# Extensions
#
maven.installation.extensions = ${maven.installation.conf}/extensions.xml
maven.project.extensions = ${maven.project.conf}/extensions.xml
maven.user.extensions = ${maven.user.conf}/extensions.xml

View File

@ -29,14 +29,15 @@ under the License.
|
| -s /path/to/user/settings.xml
|
| 2. Global Level. This settings.xml file provides configuration for all Maven
| 2. Installation Level.
| This settings.xml file provides configuration for all Maven
| users on a machine (assuming they're all using the same Maven
| installation). It's normally provided in
| ${maven.conf}/settings.xml.
| ${maven.installation.conf}/settings.xml.
|
| NOTE: This location can be overridden with the CLI option:
|
| -gs /path/to/global/settings.xml
| -is /path/to/installation/settings.xml
|
| The sections in this sample file are intended to give you a running start at
| getting the most out of your Maven installation. Where appropriate, the default

View File

@ -29,14 +29,15 @@ under the License.
|
| -t /path/to/user/toolchains.xml
|
| 2. Global Level. This toolchains.xml file provides configuration for all Maven
| 2. Installation Level.
| This toolchains.xml file provides configuration for all Maven
| users on a machine (assuming they're all using the same Maven
| installation). It's normally provided in
| ${maven.conf}/toolchains.xml.
| ${maven.installation.conf}/toolchains.xml.
|
| NOTE: This location can be overridden with the CLI option:
|
| -gt /path/to/global/toolchains.xml
| -it /path/to/installation/toolchains.xml
|
| The sections in this sample file are intended to give you a running start at
| getting the most out of your Maven installation.

View File

@ -29,10 +29,10 @@ import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* Tests that the global settings.xml shipped with the distribution is in good state.
* Tests that the installation settings.xml shipped with the distribution is in good state.
*
*/
class GlobalSettingsTest {
class InstallationSettingsTest {
@Test
void testValidGlobalSettings() throws Exception {

View File

@ -0,0 +1,267 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.api;
import org.apache.maven.api.annotations.Config;
/**
* Configuration constants.
*/
public final class Constants {
/**
* Maven home.
*
* @since 3.0.0
*/
@Config(readOnly = true)
public static final String MAVEN_HOME = "maven.home";
/**
* Maven installation configuration directory.
*
* @since 4.0.0
*/
@Config(defaultValue = "${maven.home}/conf")
public static final String MAVEN_INSTALLATION_CONF = "maven.installation.conf";
/**
* Maven user configuration directory.
*
* @since 4.0.0
*/
@Config(defaultValue = "${user.home}/.m2")
public static final String MAVEN_USER_CONF = "maven.user.conf";
/**
* Maven project configuration directory.
*
* @since 4.0.0
*/
@Config(defaultValue = "${session.rootDirectory}/.mvn")
public static final String MAVEN_PROJECT_CONF = "maven.project.conf";
/**
* Maven local repository.
*
* @since 3.0.0
*/
@Config(defaultValue = "${maven.user.conf}/repository")
public static final String MAVEN_REPO_LOCAL = "maven.repo.local";
/**
* Maven installation settings.
*
* @since 4.0.0
*/
@Config(defaultValue = "${maven.installation.conf}/settings.xml")
public static final String MAVEN_INSTALLATION_SETTINGS = "maven.installation.settings";
/**
* Maven user settings.
*
* @since 4.0.0
*/
@Config(defaultValue = "${maven.user.conf}/settings.xml")
public static final String MAVEN_USER_SETTINGS = "maven.user.settings";
/**
* Maven project settings.
*
* @since 4.0.0
*/
@Config(defaultValue = "${maven.project.conf}/settings.xml")
public static final String MAVEN_PROJECT_SETTINGS = "maven.project.settings";
/**
* Maven installation extensions.
*
* @since 4.0.0
*/
@Config(defaultValue = "${maven.installation.conf}/extensions.xml")
public static final String MAVEN_INSTALLATION_EXTENSIONS = "maven.installation.extensions";
/**
* Maven user extensions.
*
* @since 4.0.0
*/
@Config(defaultValue = "${maven.user.conf}/extensions.xml")
public static final String MAVEN_USER_EXTENSIONS = "maven.user.extensions";
/**
* Maven project extensions.
*
* @since 4.0.0
*/
@Config(defaultValue = "${maven.project.conf}/extensions.xml")
public static final String MAVEN_PROJECT_EXTENSIONS = "maven.project.extensions";
/**
* Maven installation toolchains.
*
* @since 4.0.0
*/
@Config(defaultValue = "${maven.installation.conf}/toolchains.xml")
public static final String MAVEN_INSTALLATION_TOOLCHAINS = "maven.installation.toolchains";
/**
* Maven user toolchains.
*
* @since 4.0.0
*/
@Config(defaultValue = "${maven.user.home}/toolchains.xml")
public static final String MAVEN_USER_TOOLCHAINS = "maven.user.toolchains";
/**
* Extensions class path.
*/
@Config
public static final String MAVEN_EXT_CLASS_PATH = "maven.ext.class.path";
/**
* Maven output color mode.
* Allowed values are <code>auto</code>, <code>always</code>, <code>never</code>.
*
* @since 4.0.0
*/
@Config(defaultValue = "auto")
public static final String MAVEN_STYLE_COLOR_PROPERTY = "maven.style.color";
/**
* Build timestamp format.
*
* @since 3.0.0
*/
@Config(source = Config.Source.MODEL, defaultValue = "yyyy-MM-dd'T'HH:mm:ssXXX")
public static final String MAVEN_BUILD_TIMESTAMP_FORMAT = "maven.build.timestamp.format";
/**
* User controlled relocations.
* This property is a comma separated list of entries with the syntax <code>GAV&gt;GAV</code>.
* The first <code>GAV</code> can contain <code>*</code> for any elem (so <code>*:*:*</code> would mean ALL, something
* you don't want). The second <code>GAV</code> is either fully specified, or also can contain <code>*</code>,
* then it behaves as "ordinary relocation": the coordinate is preserved from relocated artifact.
* Finally, if right hand <code>GAV</code> is absent (line looks like <code>GAV&gt;</code>), the left hand matching
* <code>GAV</code> is banned fully (from resolving).
* <br/>
* Note: the <code>&gt;</code> means project level, while <code>&gt;&gt;</code> means global (whole session level,
* so even plugins will get relocated artifacts) relocation.
* <br/>
* For example,
* <pre>maven.relocations.entries = org.foo:*:*>, \\<br/> org.here:*:*>org.there:*:*, \\<br/> javax.inject:javax.inject:1>>jakarta.inject:jakarta.inject:1.0.5</pre>
* means: 3 entries, ban <code>org.foo group</code> (exactly, so <code>org.foo.bar</code> is allowed),
* relocate <code>org.here</code> to <code>org.there</code> and finally globally relocate (see <code>&gt;&gt;</code> above)
* <code>javax.inject:javax.inject:1</code> to <code>jakarta.inject:jakarta.inject:1.0.5</code>.
*
* @since 4.0.0
*/
@Config
public static final String MAVEN_RELOCATIONS_ENTRIES = "maven.relocations.entries";
/**
* User property for version filters expression, a semicolon separated list of filters to apply. By default, no version
* filter is applied (like in Maven 3).
* <br/>
* Supported filters:
* <ul>
* <li>"h" or "h(num)" - highest version or top list of highest ones filter</li>
* <li>"l" or "l(num)" - lowest version or bottom list of lowest ones filter</li>
* <li>"s" - contextual snapshot filter</li>
* <li>"e(G:A:V)" - predicate filter (leaves out G:A:V from range, if hit, V can be range)</li>
* </ul>
* Example filter expression: <code>"h(5);s;e(org.foo:bar:1)</code> will cause: ranges are filtered for "top 5" (instead
* full range), snapshots are banned if root project is not a snapshot, and if range for <code>org.foo:bar</code> is
* being processed, version 1 is omitted.
*
* @since 4.0.0
*/
@Config
public static final String MAVEN_VERSION_FILTERS = "maven.versionFilters";
/**
* User property for chained LRM: list of "tail" local repository paths (separated by comma), to be used with
* {@code org.eclipse.aether.util.repository.ChainedLocalRepositoryManager}.
* Default value: <code>null</code>, no chained LRM is used.
*
* @since 3.9.0
*/
@Config
public static final String MAVEN_REPO_LOCAL_TAIL = "maven.repo.local.tail";
/**
* User property for reverse dependency tree. If enabled, Maven will record ".tracking" directory into local
* repository with "reverse dependency tree", essentially explaining WHY given artifact is present in local
* repository.
* Default: <code>false</code>, will not record anything.
*
* @since 3.9.0
*/
@Config(defaultValue = "false")
public static final String MAVEN_REPO_LOCAL_RECORD_REVERSE_TREE = "maven.repo.local.recordReverseTree";
/**
* User property for selecting dependency manager behaviour regarding transitive dependencies and dependency
* management entries in their POMs. Maven 3 targeted full backward compatibility with Maven2, hence it ignored
* dependency management entries in transitive dependency POMs. Maven 4 enables "transitivity" by default, hence
* unlike Maven2, obeys dependency management entries deep in dependency graph as well.
* <br/>
* Default: <code>"true"</code>.
*
* @since 4.0.0
*/
@Config(defaultValue = "true")
public static final String MAVEN_RESOLVER_DEPENDENCY_MANAGER_TRANSITIVITY =
"maven.resolver.dependencyManagerTransitivity";
/**
* Resolver transport to use.
* Can be <code>default</code>, <code>wagon</code>, <code>apache</code>, <code>jdk</code> or <code>auto</code>.
*
* @since 4.0.0
*/
@Config(defaultValue = "default")
public static final String MAVEN_RESOLVER_TRANSPORT = "maven.resolver.transport";
/**
* Plugin validation level.
*
* @since 3.9.2
*/
@Config(defaultValue = "inline")
public static final String MAVEN_PLUGIN_VALIDATION = "maven.plugin.validation";
/**
* Plugin validation exclusions.
*
* @since 3.9.6
*/
@Config
public static final String MAVEN_PLUGIN_VALIDATION_EXCLUDES = "maven.plugin.validation.excludes";
/**
* ProjectBuilder parallelism.
*
* @since 4.0.0
*/
@Config(type = "java.lang.Integer", defaultValue = "cores/2 + 1")
public static final String MAVEN_PROJECT_BUILDER_PARALLELISM = "maven.projectBuilder.parallelism";
private Constants() {}
}

View File

@ -28,7 +28,7 @@ import org.apache.maven.api.annotations.Nonnull;
import org.apache.maven.api.settings.Settings;
/**
* Builds the effective settings from a user settings file and/or a global settings file.
* Builds the effective settings from a user settings file and/or a installation settings file.
*
* @since 4.0.0
*/
@ -53,8 +53,8 @@ public interface SettingsBuilder extends Service {
*/
@Nonnull
default SettingsBuilderResult build(
@Nonnull Session session, @Nonnull Source globalSettingsSource, @Nonnull Source userSettingsSource) {
return build(session, globalSettingsSource, null, userSettingsSource);
@Nonnull Session session, @Nonnull Source installationSettingsSource, @Nonnull Source userSettingsSource) {
return build(session, installationSettingsSource, null, userSettingsSource);
}
/**
@ -65,8 +65,8 @@ public interface SettingsBuilder extends Service {
*/
@Nonnull
default SettingsBuilderResult build(
@Nonnull Session session, @Nonnull Path globalSettingsPath, @Nonnull Path userSettingsPath) {
return build(session, globalSettingsPath, null, userSettingsPath);
@Nonnull Session session, @Nonnull Path installationSettingsPath, @Nonnull Path userSettingsPath) {
return build(session, installationSettingsPath, null, userSettingsPath);
}
/**
@ -78,11 +78,11 @@ public interface SettingsBuilder extends Service {
@Nonnull
default SettingsBuilderResult build(
@Nonnull Session session,
@Nonnull Source globalSettingsSource,
@Nonnull Source installationSettingsSource,
@Nonnull Source projectSettingsSource,
@Nonnull Source userSettingsSource) {
return build(
SettingsBuilderRequest.build(session, globalSettingsSource, projectSettingsSource, userSettingsSource));
return build(SettingsBuilderRequest.build(
session, installationSettingsSource, projectSettingsSource, userSettingsSource));
}
/**
@ -94,10 +94,11 @@ public interface SettingsBuilder extends Service {
@Nonnull
default SettingsBuilderResult build(
@Nonnull Session session,
@Nonnull Path globalSettingsPath,
@Nonnull Path installationSettingsPath,
@Nonnull Path projectSettingsPath,
@Nonnull Path userSettingsPath) {
return build(SettingsBuilderRequest.build(session, globalSettingsPath, projectSettingsPath, userSettingsPath));
return build(
SettingsBuilderRequest.build(session, installationSettingsPath, projectSettingsPath, userSettingsPath));
}
/**
@ -115,7 +116,7 @@ public interface SettingsBuilder extends Service {
* Validate the specified settings.
*
* @param settings The settings to validate, must not be {@code null}.
* @param isProjectSettings Boolean indicating if the validation is for project settings or user / global settings.
* @param isProjectSettings Boolean indicating if the validation is for project settings or user / installation settings.
* @return The list of problems that were encountered, must not be {@code null}.
*/
@Nonnull

View File

@ -42,12 +42,12 @@ public interface SettingsBuilderRequest {
Session getSession();
/**
* Gets the global settings source.
* Gets the installation settings source.
*
* @return the global settings source or {@code null} if none
* @return the installation settings source or {@code null} if none
*/
@Nonnull
Optional<Source> getGlobalSettingsSource();
Optional<Source> getInstallationSettingsSource();
/**
* Gets the project settings source.
@ -67,25 +67,25 @@ public interface SettingsBuilderRequest {
@Nonnull
static SettingsBuilderRequest build(
@Nonnull Session session, @Nonnull Source globalSettingsSource, @Nonnull Source userSettingsSource) {
return build(session, globalSettingsSource, null, userSettingsSource);
@Nonnull Session session, @Nonnull Source installationSettingsSource, @Nonnull Source userSettingsSource) {
return build(session, installationSettingsSource, null, userSettingsSource);
}
@Nonnull
static SettingsBuilderRequest build(
@Nonnull Session session, @Nonnull Path globalSettingsPath, @Nonnull Path userSettingsPath) {
return build(session, Source.fromPath(globalSettingsPath), null, Source.fromPath(userSettingsPath));
@Nonnull Session session, @Nonnull Path installationSettingsPath, @Nonnull Path userSettingsPath) {
return build(session, Source.fromPath(installationSettingsPath), null, Source.fromPath(userSettingsPath));
}
@Nonnull
static SettingsBuilderRequest build(
@Nonnull Session session,
@Nullable Source globalSettingsSource,
@Nullable Source installationSettingsSource,
@Nullable Source projectSettingsSource,
@Nullable Source userSettingsSource) {
return builder()
.session(nonNull(session, "session cannot be null"))
.globalSettingsSource(globalSettingsSource)
.installationSettingsSource(installationSettingsSource)
.projectSettingsSource(projectSettingsSource)
.userSettingsSource(userSettingsSource)
.build();
@ -94,14 +94,14 @@ public interface SettingsBuilderRequest {
@Nonnull
static SettingsBuilderRequest build(
@Nonnull Session session,
@Nullable Path globalSettingsPath,
@Nullable Path installationSettingsPath,
@Nullable Path projectSettingsPath,
@Nullable Path userSettingsPath) {
return builder()
.session(nonNull(session, "session cannot be null"))
.globalSettingsSource(
globalSettingsPath != null && Files.exists(globalSettingsPath)
? Source.fromPath(globalSettingsPath)
.installationSettingsSource(
installationSettingsPath != null && Files.exists(installationSettingsPath)
? Source.fromPath(installationSettingsPath)
: null)
.projectSettingsSource(
projectSettingsPath != null && Files.exists(projectSettingsPath)
@ -122,7 +122,7 @@ public interface SettingsBuilderRequest {
@NotThreadSafe
class SettingsBuilderRequestBuilder {
Session session;
Source globalSettingsSource;
Source installationSettingsSource;
Source projectSettingsSource;
Source userSettingsSource;
@ -131,8 +131,8 @@ public interface SettingsBuilderRequest {
return this;
}
public SettingsBuilderRequestBuilder globalSettingsSource(Source globalSettingsSource) {
this.globalSettingsSource = globalSettingsSource;
public SettingsBuilderRequestBuilder installationSettingsSource(Source installationSettingsSource) {
this.installationSettingsSource = installationSettingsSource;
return this;
}
@ -148,30 +148,30 @@ public interface SettingsBuilderRequest {
public SettingsBuilderRequest build() {
return new DefaultSettingsBuilderRequest(
session, globalSettingsSource, projectSettingsSource, userSettingsSource);
session, installationSettingsSource, projectSettingsSource, userSettingsSource);
}
private static class DefaultSettingsBuilderRequest extends BaseRequest implements SettingsBuilderRequest {
private final Source globalSettingsSource;
private final Source installationSettingsSource;
private final Source projectSettingsSource;
private final Source userSettingsSource;
@SuppressWarnings("checkstyle:ParameterNumber")
DefaultSettingsBuilderRequest(
@Nonnull Session session,
@Nullable Source globalSettingsSource,
@Nullable Source installationSettingsSource,
@Nullable Source projectSettingsSource,
@Nullable Source userSettingsSource) {
super(session);
this.globalSettingsSource = globalSettingsSource;
this.installationSettingsSource = installationSettingsSource;
this.projectSettingsSource = projectSettingsSource;
this.userSettingsSource = userSettingsSource;
}
@Nonnull
@Override
public Optional<Source> getGlobalSettingsSource() {
return Optional.ofNullable(globalSettingsSource);
public Optional<Source> getInstallationSettingsSource() {
return Optional.ofNullable(installationSettingsSource);
}
@Nonnull

View File

@ -24,7 +24,7 @@ import org.apache.maven.api.annotations.Experimental;
import org.apache.maven.api.annotations.Nonnull;
/**
* Builds the effective toolchains from a user toolchains file and/or a global toolchains file.
* Builds the effective toolchains from a user toolchains file and/or an installation toolchains file.
*
* @since 4.0.0
*/
@ -44,7 +44,7 @@ public interface ToolchainsBuilder extends Service {
* Builds the effective toolchains for the specified toolchains sources.
*
* @param session the {@link Session}, must not be {@code null}
* @param globalToolchainsSource The {@link Source} pointing to the global toolchains, must not be {@code null}
* @param installationToolchainsFile The {@link Source} pointing to the installation toolchains, must not be {@code null}
* @param userToolchainsSource The {@link Source} pointing to the user toolchains, must not be {@code null}
* @throws ToolchainsBuilderException if the project cannot be created
* @throws IllegalArgumentException if an argument is {@code null} or invalid
@ -52,7 +52,9 @@ public interface ToolchainsBuilder extends Service {
*/
@Nonnull
default ToolchainsBuilderResult build(
@Nonnull Session session, @Nonnull Source globalToolchainsSource, @Nonnull Source userToolchainsSource) {
return build(ToolchainsBuilderRequest.build(session, globalToolchainsSource, userToolchainsSource));
@Nonnull Session session,
@Nonnull Source installationToolchainsFile,
@Nonnull Source userToolchainsSource) {
return build(ToolchainsBuilderRequest.build(session, installationToolchainsFile, userToolchainsSource));
}
}

View File

@ -40,12 +40,12 @@ public interface ToolchainsBuilderRequest {
Session getSession();
/**
* Gets the global Toolchains source.
* Gets the installation Toolchains source.
*
* @return the global Toolchains source or {@code null} if none
* @return the installation Toolchains source or {@code null} if none
*/
@Nonnull
Optional<Source> getGlobalToolchainsSource();
Optional<Source> getInstallationToolchainsSource();
/**
* Gets the user Toolchains source.
@ -57,22 +57,24 @@ public interface ToolchainsBuilderRequest {
@Nonnull
static ToolchainsBuilderRequest build(
@Nonnull Session session, @Nullable Source globalToolchainsSource, @Nullable Source userToolchainsSource) {
@Nonnull Session session,
@Nullable Source installationToolchainsFile,
@Nullable Source userToolchainsSource) {
return builder()
.session(nonNull(session, "session cannot be null"))
.globalToolchainsSource(globalToolchainsSource)
.installationToolchainsSource(installationToolchainsFile)
.userToolchainsSource(userToolchainsSource)
.build();
}
@Nonnull
static ToolchainsBuilderRequest build(
@Nonnull Session session, @Nullable Path globalToolchainsPath, @Nullable Path userToolchainsPath) {
@Nonnull Session session, @Nullable Path installationToolchainsFile, @Nullable Path userToolchainsPath) {
return builder()
.session(nonNull(session, "session cannot be null"))
.globalToolchainsSource(
globalToolchainsPath != null && Files.exists(globalToolchainsPath)
? Source.fromPath(globalToolchainsPath)
.installationToolchainsSource(
installationToolchainsFile != null && Files.exists(installationToolchainsFile)
? Source.fromPath(installationToolchainsFile)
: null)
.userToolchainsSource(
userToolchainsPath != null && Files.exists(userToolchainsPath)
@ -89,7 +91,7 @@ public interface ToolchainsBuilderRequest {
@NotThreadSafe
class ToolchainsBuilderRequestBuilder {
Session session;
Source globalToolchainsSource;
Source installationToolchainsSource;
Source userToolchainsSource;
public ToolchainsBuilderRequestBuilder session(Session session) {
@ -97,8 +99,8 @@ public interface ToolchainsBuilderRequest {
return this;
}
public ToolchainsBuilderRequestBuilder globalToolchainsSource(Source globalToolchainsSource) {
this.globalToolchainsSource = globalToolchainsSource;
public ToolchainsBuilderRequestBuilder installationToolchainsSource(Source installationToolchainsSource) {
this.installationToolchainsSource = installationToolchainsSource;
return this;
}
@ -109,27 +111,27 @@ public interface ToolchainsBuilderRequest {
public ToolchainsBuilderRequest build() {
return new ToolchainsBuilderRequestBuilder.DefaultToolchainsBuilderRequest(
session, globalToolchainsSource, userToolchainsSource);
session, installationToolchainsSource, userToolchainsSource);
}
private static class DefaultToolchainsBuilderRequest extends BaseRequest implements ToolchainsBuilderRequest {
private final Source globalToolchainsSource;
private final Source installationToolchainsSource;
private final Source userToolchainsSource;
@SuppressWarnings("checkstyle:ParameterNumber")
DefaultToolchainsBuilderRequest(
@Nonnull Session session,
@Nullable Source globalToolchainsSource,
@Nullable Source installationToolchainsSource,
@Nullable Source userToolchainsSource) {
super(session);
this.globalToolchainsSource = globalToolchainsSource;
this.installationToolchainsSource = installationToolchainsSource;
this.userToolchainsSource = userToolchainsSource;
}
@Nonnull
@Override
public Optional<Source> getGlobalToolchainsSource() {
return Optional.ofNullable(globalToolchainsSource);
public Optional<Source> getInstallationToolchainsSource() {
return Optional.ofNullable(installationToolchainsSource);
}
@Nonnull

View File

@ -0,0 +1,45 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.api.annotations;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Experimental
@Documented
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface Config {
Source source() default Source.USER_PROPERTIES;
String type() default "java.lang.String";
String defaultValue() default "";
boolean readOnly() default false;
enum Source {
USER_PROPERTIES,
MODEL
}
}

View File

@ -44,8 +44,7 @@
<name>TrackableBase</name>
<version>1.0.0+</version>
<description>
common base class that contains code to track the source for
this instance (USER|GLOBAL)
Common base class that contains code to track the source for this instance.
</description>
<codeSegments>
<codeSegment>

View File

@ -51,7 +51,7 @@
<name>TrackableBase</name>
<version>1.1.0+</version>
<description>
Common base class that contains code to track the source for this instance (USER|GLOBAL)
Common base class that contains code to track the source for this instance.
</description>
<codeSegments>
<codeSegment>

View File

@ -66,8 +66,8 @@ public class DefaultSettingsBuilder implements SettingsBuilder {
public SettingsBuilderResult build(SettingsBuilderRequest request) throws SettingsBuilderException {
List<BuilderProblem> problems = new ArrayList<>();
Source globalSource = request.getGlobalSettingsSource().orElse(null);
Settings global = readSettings(globalSource, false, request, problems);
Source installationSource = request.getInstallationSettingsSource().orElse(null);
Settings installation = readSettings(installationSource, false, request, problems);
Source projectSource = request.getProjectSettingsSource().orElse(null);
Settings project = readSettings(projectSource, true, request, problems);
@ -76,7 +76,7 @@ public class DefaultSettingsBuilder implements SettingsBuilder {
Settings user = readSettings(userSource, false, request, problems);
Settings effective =
settingsMerger.merge(user, settingsMerger.merge(project, global, false, null), false, null);
settingsMerger.merge(user, settingsMerger.merge(project, installation, false, null), false, null);
// If no repository is defined in the user/global settings,
// it means that we have "old" settings (as those are new in 4.0)

View File

@ -57,13 +57,13 @@ public class DefaultToolchainsBuilder implements ToolchainsBuilder {
public ToolchainsBuilderResult build(ToolchainsBuilderRequest request) throws ToolchainsBuilderException {
List<BuilderProblem> problems = new ArrayList<>();
Source globalSource = request.getGlobalToolchainsSource().orElse(null);
PersistedToolchains global = readToolchains(globalSource, request, problems);
Source installationSource = request.getInstallationToolchainsSource().orElse(null);
PersistedToolchains installation = readToolchains(installationSource, request, problems);
Source userSource = request.getUserToolchainsSource().orElse(null);
PersistedToolchains user = readToolchains(userSource, request, problems);
PersistedToolchains effective = toolchainsMerger.merge(user, global, false, null);
PersistedToolchains effective = toolchainsMerger.merge(user, installation, false, null);
if (hasErrors(problems)) {
throw new ToolchainsBuilderException("Error building toolchains", problems);

View File

@ -26,18 +26,18 @@ import java.util.Map;
import java.util.Properties;
import java.util.TimeZone;
import org.apache.maven.api.Constants;
/**
* MavenBuildTimestamp
*/
public class MavenBuildTimestamp {
// ISO 8601-compliant timestamp for machine readability
public static final String DEFAULT_BUILD_TIMESTAMP_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
public static final String BUILD_TIMESTAMP_FORMAT_PROPERTY = "maven.build.timestamp.format";
public static final String DEFAULT_BUILD_TIMESTAMP_FORMAT = "yyyy-MM-dd'T'HH:mm:ssXXX";
public static final TimeZone DEFAULT_BUILD_TIME_ZONE = TimeZone.getTimeZone("Etc/UTC");
private String formattedTimestamp;
private final String formattedTimestamp;
public MavenBuildTimestamp() {
this(Instant.now());
@ -48,7 +48,7 @@ public class MavenBuildTimestamp {
}
public MavenBuildTimestamp(Instant time, Map<String, String> properties) {
this(time, properties != null ? properties.get(BUILD_TIMESTAMP_FORMAT_PROPERTY) : null);
this(time, properties != null ? properties.get(Constants.MAVEN_BUILD_TIMESTAMP_FORMAT) : null);
}
/**
@ -58,7 +58,7 @@ public class MavenBuildTimestamp {
*/
@Deprecated
public MavenBuildTimestamp(Instant time, Properties properties) {
this(time, properties != null ? properties.getProperty(BUILD_TIMESTAMP_FORMAT_PROPERTY) : null);
this(time, properties != null ? properties.getProperty(Constants.MAVEN_BUILD_TIMESTAMP_FORMAT) : null);
}
public MavenBuildTimestamp(Instant time, String timestampFormat) {

View File

@ -25,6 +25,7 @@ import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.maven.api.Constants;
import org.apache.maven.api.di.Named;
import org.apache.maven.api.di.Priority;
import org.apache.maven.api.di.Singleton;
@ -51,8 +52,6 @@ public final class UserPropertiesArtifactRelocationSource implements MavenArtifa
public static final String NAME = "userProperties";
private static final Logger LOGGER = LoggerFactory.getLogger(UserPropertiesArtifactRelocationSource.class);
private static final String CONFIG_PROP_RELOCATIONS_ENTRIES = "maven.relocations.entries";
private static final Artifact SENTINEL = new DefaultArtifact("org.apache.maven.banned:user-relocation:1.0");
@Override
@ -153,7 +152,7 @@ public final class UserPropertiesArtifactRelocationSource implements MavenArtifa
}
private Relocations parseRelocations(RepositorySystemSession session) {
String relocationsEntries = (String) session.getConfigProperties().get(CONFIG_PROP_RELOCATIONS_ENTRIES);
String relocationsEntries = (String) session.getConfigProperties().get(Constants.MAVEN_RELOCATIONS_ENTRIES);
if (relocationsEntries == null) {
return null;
}

View File

@ -97,11 +97,11 @@ public class DefaultMavenExecutionRequest implements MavenExecutionRequest {
private File projectSettingsFile;
private File globalSettingsFile;
private File installationSettingsFile;
private File userToolchainsFile;
private File globalToolchainsFile;
private File installationToolchainsFile;
// ----------------------------------------------------------------------------
// Request
@ -190,9 +190,9 @@ public class DefaultMavenExecutionRequest implements MavenExecutionRequest {
copy.setPluginGroups(original.getPluginGroups());
copy.setProjectPresent(original.isProjectPresent());
copy.setUserSettingsFile(original.getUserSettingsFile());
copy.setGlobalSettingsFile(original.getGlobalSettingsFile());
copy.setInstallationSettingsFile(original.getInstallationSettingsFile());
copy.setUserToolchainsFile(original.getUserToolchainsFile());
copy.setGlobalToolchainsFile(original.getGlobalToolchainsFile());
copy.setInstallationToolchainsFile(original.getInstallationToolchainsFile());
copy.setBaseDirectory((original.getBaseDirectory() != null) ? new File(original.getBaseDirectory()) : null);
copy.setGoals(original.getGoals());
copy.setRecursive(original.isRecursive());
@ -866,13 +866,25 @@ public class DefaultMavenExecutionRequest implements MavenExecutionRequest {
}
@Override
@Deprecated
public File getGlobalSettingsFile() {
return globalSettingsFile;
return getInstallationSettingsFile();
}
@Override
@Deprecated
public MavenExecutionRequest setGlobalSettingsFile(File globalSettingsFile) {
this.globalSettingsFile = globalSettingsFile;
return setInstallationSettingsFile(globalSettingsFile);
}
@Override
public File getInstallationSettingsFile() {
return installationSettingsFile;
}
@Override
public MavenExecutionRequest setInstallationSettingsFile(File installationSettingsFile) {
this.installationSettingsFile = installationSettingsFile;
return this;
}
@ -890,13 +902,26 @@ public class DefaultMavenExecutionRequest implements MavenExecutionRequest {
}
@Override
@Deprecated
public File getGlobalToolchainsFile() {
return globalToolchainsFile;
return installationToolchainsFile;
}
@Override
public MavenExecutionRequest setGlobalToolchainsFile(File globalToolchainsFile) {
this.globalToolchainsFile = globalToolchainsFile;
@Deprecated
public MavenExecutionRequest setGlobalToolchainsFile(File installationToolchainsFile) {
this.installationToolchainsFile = installationToolchainsFile;
return this;
}
@Override
public File getInstallationToolchainsFile() {
return installationToolchainsFile;
}
@Override
public MavenExecutionRequest setInstallationToolchainsFile(File installationToolchainsFile) {
this.installationToolchainsFile = installationToolchainsFile;
return this;
}

View File

@ -28,6 +28,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.maven.api.Constants;
import org.apache.maven.artifact.InvalidRepositoryException;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.bridge.MavenRepositorySystem;
@ -125,7 +126,11 @@ public class DefaultMavenExecutionRequestPopulator implements MavenExecutionRequ
}
if (localRepositoryPath == null || localRepositoryPath.isEmpty()) {
localRepositoryPath = new File(System.getProperty("user.home"), ".m2/repository").getAbsolutePath();
String path = request.getUserProperties().getProperty(Constants.MAVEN_USER_CONF);
if (path == null) {
path = request.getSystemProperties().getProperty("user.home") + File.separator + ".m2";
}
localRepositoryPath = new File(path, "repository").getAbsolutePath();
}
try {

View File

@ -426,10 +426,16 @@ public interface MavenExecutionRequest {
MavenExecutionRequest setProjectSettingsFile(File projectSettingsFile);
@Deprecated
File getGlobalSettingsFile();
@Deprecated
MavenExecutionRequest setGlobalSettingsFile(File globalSettingsFile);
File getInstallationSettingsFile();
MavenExecutionRequest setInstallationSettingsFile(File installationSettingsFile);
MavenExecutionRequest addRemoteRepository(ArtifactRepository repository);
MavenExecutionRequest addPluginArtifactRepository(ArtifactRepository repository);
@ -467,7 +473,9 @@ public interface MavenExecutionRequest {
*
* @return the global toolchains file
* @since 3.3.0
* @deprecated use {@link #getInstallationToolchainsFile()}
*/
@Deprecated
File getGlobalToolchainsFile();
/**
@ -475,9 +483,27 @@ public interface MavenExecutionRequest {
* @param globalToolchainsFile the global toolchains file
* @return this request
* @since 3.3.0
* @deprecated use {@link #setInstallationToolchainsFile(File)}
*/
@Deprecated
MavenExecutionRequest setGlobalToolchainsFile(File globalToolchainsFile);
/**
*
*
* @return the installation toolchains file
* @since 4.0.0
*/
File getInstallationToolchainsFile();
/**
*
* @param installationToolchainsFile the installation toolchains file
* @return this request
* @since 4.0.0
*/
MavenExecutionRequest setInstallationToolchainsFile(File installationToolchainsFile);
ExecutionListener getExecutionListener();
MavenExecutionRequest setExecutionListener(ExecutionListener executionListener);

View File

@ -30,6 +30,7 @@ import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.apache.maven.api.Constants;
import org.apache.maven.api.di.Inject;
import org.apache.maven.api.di.Named;
import org.apache.maven.api.di.Singleton;
@ -68,7 +69,6 @@ import org.eclipse.aether.util.graph.version.LowestVersionFilter;
import org.eclipse.aether.util.graph.version.PredicateVersionFilter;
import org.eclipse.aether.util.listener.ChainedRepositoryListener;
import org.eclipse.aether.util.repository.AuthenticationBuilder;
import org.eclipse.aether.util.repository.ChainedLocalRepositoryManager;
import org.eclipse.aether.util.repository.DefaultAuthenticationSelector;
import org.eclipse.aether.util.repository.DefaultMirrorSelector;
import org.eclipse.aether.util.repository.DefaultProxySelector;
@ -89,66 +89,14 @@ import static java.util.Objects.requireNonNull;
@Named
@Singleton
public class DefaultRepositorySystemSessionFactory implements RepositorySystemSessionFactory {
/**
* User property for version filters expression, a semicolon separated list of filters to apply. By default, no version
* filter is applied (like in Maven 3).
* <p>
* Supported filters:
* <ul>
* <li>"h" or "h(num)" - highest version or top list of highest ones filter</li>
* <li>"l" or "l(num)" - lowest version or bottom list of lowest ones filter</li>
* <li>"s" - contextual snapshot filter</li>
* <li>"e(G:A:V)" - predicate filter (leaves out G:A:V from range, if hit, V can be range)</li>
* </ul>
* Example filter expression: {@code "h(5);s;e(org.foo:bar:1)} will cause: ranges are filtered for "top 5" (instead
* full range), snapshots are banned if root project is not a snapshot, and if range for {@code org.foo:bar} is
* being processed, version 1 is omitted.
*
* @since 4.0.0
*/
private static final String MAVEN_VERSION_FILTERS = "maven.versionFilters";
/**
* User property for chained LRM: list of "tail" local repository paths (separated by comma), to be used with
* {@link ChainedLocalRepositoryManager}.
* Default value: {@code null}, no chained LRM is used.
*
* @since 3.9.0
*/
private static final String MAVEN_REPO_LOCAL_TAIL = "maven.repo.local.tail";
public static final String MAVEN_RESOLVER_TRANSPORT_DEFAULT = "default";
/**
* User property for reverse dependency tree. If enabled, Maven will record ".tracking" directory into local
* repository with "reverse dependency tree", essentially explaining WHY given artifact is present in local
* repository.
* Default: {@code false}, will not record anything.
*
* @since 3.9.0
*/
private static final String MAVEN_REPO_LOCAL_RECORD_REVERSE_TREE = "maven.repo.local.recordReverseTree";
public static final String MAVEN_RESOLVER_TRANSPORT_WAGON = "wagon";
/**
* User property for selecting dependency manager behaviour regarding transitive dependencies and dependency
* management entries in their POMs. Maven 3 targeted full backward compatibility with Maven2, hence it ignored
* dependency management entries in transitive dependency POMs. Maven 4 enables "transitivity" by default, hence
* unlike Maven2, obeys dependency management entries deep in dependency graph as well.
* <p>
* Default: {@code "true"}.
*
* @since 4.0.0
*/
private static final String MAVEN_RESOLVER_DEPENDENCY_MANAGER_TRANSITIVITY_KEY =
"maven.resolver.dependencyManagerTransitivity";
public static final String MAVEN_RESOLVER_TRANSPORT_APACHE = "apache";
private static final String MAVEN_RESOLVER_TRANSPORT_KEY = "maven.resolver.transport";
private static final String MAVEN_RESOLVER_TRANSPORT_DEFAULT = "default";
private static final String MAVEN_RESOLVER_TRANSPORT_WAGON = "wagon";
private static final String MAVEN_RESOLVER_TRANSPORT_APACHE = "apache";
private static final String MAVEN_RESOLVER_TRANSPORT_JDK = "jdk";
public static final String MAVEN_RESOLVER_TRANSPORT_JDK = "jdk";
/**
* This name for Apache HttpClient transport is deprecated.
@ -158,7 +106,7 @@ public class DefaultRepositorySystemSessionFactory implements RepositorySystemSe
@Deprecated
private static final String MAVEN_RESOLVER_TRANSPORT_NATIVE = "native";
private static final String MAVEN_RESOLVER_TRANSPORT_AUTO = "auto";
public static final String MAVEN_RESOLVER_TRANSPORT_AUTO = "auto";
private static final String WAGON_TRANSPORTER_PRIORITY_KEY = "aether.priority.WagonTransporterFactory";
@ -257,7 +205,7 @@ public class DefaultRepositorySystemSessionFactory implements RepositorySystemSe
sessionBuilder.setArtifactDescriptorPolicy(new SimpleArtifactDescriptorPolicy(
request.isIgnoreMissingArtifactDescriptor(), request.isIgnoreInvalidArtifactDescriptor()));
VersionFilter versionFilter = buildVersionFilter(mergedProps.get(MAVEN_VERSION_FILTERS));
VersionFilter versionFilter = buildVersionFilter(mergedProps.get(Constants.MAVEN_VERSION_FILTERS));
if (versionFilter != null) {
sessionBuilder.setVersionFilter(versionFilter);
}
@ -390,7 +338,8 @@ public class DefaultRepositorySystemSessionFactory implements RepositorySystemSe
}
sessionBuilder.setAuthenticationSelector(authSelector);
Object transport = mergedProps.getOrDefault(MAVEN_RESOLVER_TRANSPORT_KEY, MAVEN_RESOLVER_TRANSPORT_DEFAULT);
Object transport =
mergedProps.getOrDefault(Constants.MAVEN_RESOLVER_TRANSPORT, MAVEN_RESOLVER_TRANSPORT_DEFAULT);
if (MAVEN_RESOLVER_TRANSPORT_DEFAULT.equals(transport)) {
// The "default" mode (user did not set anything) from now on defaults to AUTO
} else if (MAVEN_RESOLVER_TRANSPORT_JDK.equals(transport)) {
@ -425,21 +374,21 @@ public class DefaultRepositorySystemSessionFactory implements RepositorySystemSe
RepositoryListener repositoryListener = eventSpyDispatcher.chainListener(new LoggingRepositoryListener(logger));
boolean recordReverseTree = Boolean.parseBoolean(
mergedProps.getOrDefault(MAVEN_REPO_LOCAL_RECORD_REVERSE_TREE, Boolean.FALSE.toString()));
mergedProps.getOrDefault(Constants.MAVEN_REPO_LOCAL_RECORD_REVERSE_TREE, Boolean.FALSE.toString()));
if (recordReverseTree) {
repositoryListener = new ChainedRepositoryListener(repositoryListener, new ReverseTreeRepositoryListener());
}
sessionBuilder.setRepositoryListener(repositoryListener);
// may be overridden
String resolverDependencyManagerTransitivity =
mergedProps.getOrDefault(MAVEN_RESOLVER_DEPENDENCY_MANAGER_TRANSITIVITY_KEY, Boolean.TRUE.toString());
String resolverDependencyManagerTransitivity = mergedProps.getOrDefault(
Constants.MAVEN_RESOLVER_DEPENDENCY_MANAGER_TRANSITIVITY, Boolean.TRUE.toString());
sessionBuilder.setDependencyManager(
supplier.getDependencyManager(Boolean.parseBoolean(resolverDependencyManagerTransitivity)));
ArrayList<Path> paths = new ArrayList<>();
paths.add(Paths.get(request.getLocalRepository().getBasedir()));
String localRepoTail = mergedProps.get(MAVEN_REPO_LOCAL_TAIL);
String localRepoTail = mergedProps.get(Constants.MAVEN_REPO_LOCAL_TAIL);
if (localRepoTail != null) {
Arrays.stream(localRepoTail.split(","))
.filter(p -> p != null && !p.trim().isEmpty())

View File

@ -38,6 +38,7 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import org.apache.maven.api.Constants;
import org.apache.maven.eventspy.AbstractEventSpy;
import org.apache.maven.execution.ExecutionEvent;
import org.apache.maven.execution.MavenSession;
@ -66,11 +67,7 @@ public final class DefaultPluginValidationManager extends AbstractEventSpy imple
private static final String PLUGIN_EXCLUDES_KEY = DefaultPluginValidationManager.class.getName() + ".excludes";
private static final String MAVEN_PLUGIN_VALIDATION_KEY = "maven.plugin.validation";
private static final String MAVEN_PLUGIN_VALIDATION_EXCLUDES_KEY = "maven.plugin.validation.excludes";
private static final ValidationReportLevel DEFAULT_VALIDATION_LEVEL = ValidationReportLevel.INLINE;
public static final ValidationReportLevel DEFAULT_VALIDATION_LEVEL = ValidationReportLevel.INLINE;
private static final Collection<ValidationReportLevel> INLINE_VALIDATION_LEVEL = Collections.unmodifiableCollection(
Arrays.asList(ValidationReportLevel.INLINE, ValidationReportLevel.BRIEF));
@ -106,7 +103,7 @@ public final class DefaultPluginValidationManager extends AbstractEventSpy imple
}
private List<String> parsePluginExcludes(RepositorySystemSession session) {
String excludes = ConfigUtils.getString(session, null, MAVEN_PLUGIN_VALIDATION_EXCLUDES_KEY);
String excludes = ConfigUtils.getString(session, null, Constants.MAVEN_PLUGIN_VALIDATION_EXCLUDES);
if (excludes == null || excludes.isEmpty()) {
return Collections.emptyList();
}
@ -122,7 +119,7 @@ public final class DefaultPluginValidationManager extends AbstractEventSpy imple
}
private ValidationReportLevel parseValidationReportLevel(RepositorySystemSession session) {
String level = ConfigUtils.getString(session, null, MAVEN_PLUGIN_VALIDATION_KEY);
String level = ConfigUtils.getString(session, null, Constants.MAVEN_PLUGIN_VALIDATION);
if (level == null || level.isEmpty()) {
return DEFAULT_VALIDATION_LEVEL;
}
@ -131,7 +128,7 @@ public final class DefaultPluginValidationManager extends AbstractEventSpy imple
} catch (IllegalArgumentException e) {
logger.warn(
"Invalid value specified for property {}: '{}'. Supported values are (case insensitive): {}",
MAVEN_PLUGIN_VALIDATION_KEY,
Constants.MAVEN_PLUGIN_VALIDATION,
level,
Arrays.toString(ValidationReportLevel.values()));
return DEFAULT_VALIDATION_LEVEL;

View File

@ -57,6 +57,7 @@ import java.util.stream.Stream;
import org.apache.maven.ProjectCycleException;
import org.apache.maven.RepositoryUtils;
import org.apache.maven.api.Constants;
import org.apache.maven.api.Session;
import org.apache.maven.api.SessionData;
import org.apache.maven.api.feature.Features;
@ -118,7 +119,7 @@ import org.slf4j.LoggerFactory;
@Named
@Singleton
public class DefaultProjectBuilder implements ProjectBuilder {
public static final String BUILDER_PARALLELISM = "maven.projectBuilder.parallelism";
public static final int DEFAULT_BUILDER_PARALLELISM = Runtime.getRuntime().availableProcessors() / 2 + 1;
private final Logger logger = LoggerFactory.getLogger(getClass());
@ -382,10 +383,7 @@ public class DefaultProjectBuilder implements ProjectBuilder {
private int getParallelism(ProjectBuildingRequest request) {
int parallelism = DEFAULT_BUILDER_PARALLELISM;
try {
String str = request.getUserProperties().getProperty(BUILDER_PARALLELISM);
if (str == null) {
str = request.getSystemProperties().getProperty(BUILDER_PARALLELISM);
}
String str = request.getUserProperties().getProperty(Constants.MAVEN_PROJECT_BUILDER_PARALLELISM);
if (str != null) {
parallelism = Integer.parseInt(str);
}

118
maven-docgen/pom.xml Normal file
View File

@ -0,0 +1,118 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.maven</groupId>
<artifactId>maven</artifactId>
<version>4.0.0-beta-4-SNAPSHOT</version>
</parent>
<artifactId>maven-docgen</artifactId>
<name>Maven Documentation Generator</name>
<properties>
<maven.deploy.skip>true</maven.deploy.skip>
</properties>
<dependencies>
<!-- all needed maven module: list them here as this module must run LAST -->
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-api-impl</artifactId>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-embedder</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.forge.roaster</groupId>
<artifactId>roaster-api</artifactId>
<version>2.29.0.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.forge.roaster</groupId>
<artifactId>roaster-jdt</artifactId>
<version>2.29.0.Final</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-utils</artifactId>
</dependency>
<!-- Not needed during compile -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.14.0</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.3.0</version>
<executions>
<execution>
<id>render-configuration-page</id>
<goals>
<goal>java</goal>
</goals>
<phase>verify</phase>
<configuration>
<mainClass>org.apache.maven.tools.CollectConfiguration</mainClass>
<arguments>
<argument>${basedir}/..</argument>
<argument>${basedir}/../src/site/markdown/configuration.md</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,363 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.tools;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UncheckedIOException;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.spi.ToolProvider;
import org.apache.maven.api.annotations.Config;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
import org.codehaus.plexus.util.io.CachingWriter;
import org.jboss.forge.roaster.Roaster;
import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.AST;
import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.ASTNode;
import org.jboss.forge.roaster._shade.org.eclipse.jdt.core.dom.Javadoc;
import org.jboss.forge.roaster.model.JavaDocCapable;
import org.jboss.forge.roaster.model.JavaDocTag;
import org.jboss.forge.roaster.model.JavaType;
import org.jboss.forge.roaster.model.impl.JavaDocImpl;
import org.jboss.forge.roaster.model.source.FieldSource;
import org.jboss.forge.roaster.model.source.JavaClassSource;
import org.jboss.forge.roaster.model.source.JavaDocSource;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Opcodes;
public class CollectConfiguration {
public static void main(String[] args) throws Exception {
try {
Path start = Paths.get(args.length > 0 ? args[0] : ".");
Path output = Paths.get(args.length > 1 ? args[1] : "output");
TreeMap<String, ConfigurationKey> discoveredKeys = new TreeMap<>();
Files.walk(start)
.map(Path::toAbsolutePath)
.filter(p -> p.getFileName().toString().endsWith(".class"))
.filter(p -> p.toString().contains("/target/classes/"))
.forEach(p -> {
processClass(p, discoveredKeys);
});
VelocityEngine velocityEngine = new VelocityEngine();
Properties properties = new Properties();
properties.setProperty("resource.loaders", "classpath");
properties.setProperty("resource.loader.classpath.class", ClasspathResourceLoader.class.getName());
velocityEngine.init(properties);
VelocityContext context = new VelocityContext();
context.put("keys", discoveredKeys.values());
try (Writer fileWriter = new CachingWriter(output, StandardCharsets.UTF_8)) {
velocityEngine.getTemplate("page.vm").merge(context, fileWriter);
}
} catch (Throwable t) {
t.printStackTrace();
throw t;
}
}
private static void processClass(Path path, Map<String, ConfigurationKey> discoveredKeys) {
try {
ClassReader classReader = new ClassReader(Files.newInputStream(path));
classReader.accept(
new ClassVisitor(Opcodes.ASM9) {
@Override
public FieldVisitor visitField(
int fieldAccess,
String fieldName,
String fieldDescriptor,
String fieldSignature,
Object fieldValue) {
return new FieldVisitor(Opcodes.ASM9) {
@Override
public AnnotationVisitor visitAnnotation(
String annotationDescriptor, boolean annotationVisible) {
if (annotationDescriptor.equals("Lorg/apache/maven/api/annotations/Config;")) {
return new AnnotationVisitor(Opcodes.ASM9) {
Map<String, Object> values = new HashMap<>();
@Override
public void visit(String name, Object value) {
values.put(name, value);
}
@Override
public void visitEnum(String name, String descriptor, String value) {
values.put(name, value);
}
@Override
public void visitEnd() {
JavaType<?> jtype = parse(Paths.get(path.toString()
.replace("/target/classes/", "/src/main/java/")
.replace(".class", ".java")));
FieldSource<JavaClassSource> f =
((JavaClassSource) jtype).getField(fieldName);
String fqName = null;
String desc = cloneJavadoc(f.getJavaDoc())
.removeAllTags()
.getFullText()
.replace("*", "\\*");
String since = getSince(f);
String source =
switch ((values.get("source") != null
? (String) values.get("source")
: Config.Source.USER_PROPERTIES.toString())
.toLowerCase()) {
case "model" -> "Model properties";
case "user_properties" -> "User properties";
default -> throw new IllegalStateException();
};
String type =
switch ((values.get("type") != null
? (String) values.get("type")
: "java.lang.String")) {
case "java.lang.String" -> "String";
case "java.lang.Integer" -> "Integer";
default -> throw new IllegalStateException();
};
discoveredKeys.put(
fieldValue.toString(),
new ConfigurationKey(
fieldValue.toString(),
values.get("defaultValue") != null
? values.get("defaultValue")
.toString()
: null,
fqName,
desc,
since,
source,
type));
}
};
}
return null;
}
};
}
},
0);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
static JavaDocSource<Object> cloneJavadoc(JavaDocSource<?> javaDoc) {
Javadoc jd = (Javadoc) javaDoc.getInternal();
return new JavaDocImpl(javaDoc.getOrigin(), (Javadoc)
ASTNode.copySubtree(AST.newAST(jd.getAST().apiLevel()), jd));
}
private static String unquote(String s) {
return (s.startsWith("\"") && s.endsWith("\"")) ? s.substring(1, s.length() - 1) : s;
}
private static JavaType<?> parse(Path path) {
try {
return Roaster.parse(path.toFile());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
private static boolean toBoolean(String value) {
return ("yes".equalsIgnoreCase(value) || "true".equalsIgnoreCase(value));
}
/**
* Would be record, but... Velocity have no idea what it is nor how to handle it.
*/
public static class ConfigurationKey {
private final String key;
private final String defaultValue;
private final String fqName;
private final String description;
private final String since;
private final String configurationSource;
private final String configurationType;
@SuppressWarnings("checkstyle:parameternumber")
public ConfigurationKey(
String key,
String defaultValue,
String fqName,
String description,
String since,
String configurationSource,
String configurationType) {
this.key = key;
this.defaultValue = defaultValue;
this.fqName = fqName;
this.description = description;
this.since = since;
this.configurationSource = configurationSource;
this.configurationType = configurationType;
}
public String getKey() {
return key;
}
public String getDefaultValue() {
return defaultValue;
}
public String getFqName() {
return fqName;
}
public String getDescription() {
return description;
}
public String getSince() {
return since;
}
public String getConfigurationSource() {
return configurationSource;
}
public String getConfigurationType() {
return configurationType;
}
}
private static String nvl(String string, String def) {
return string == null ? def : string;
}
private static boolean hasConfigurationSource(JavaDocCapable<?> javaDocCapable) {
return getTag(javaDocCapable, "@configurationSource") != null;
}
private static String getConfigurationType(JavaDocCapable<?> javaDocCapable) {
String type = getTag(javaDocCapable, "@configurationType");
if (type != null) {
String linkPrefix = "{@link ";
String linkSuffix = "}";
if (type.startsWith(linkPrefix) && type.endsWith(linkSuffix)) {
type = type.substring(linkPrefix.length(), type.length() - linkSuffix.length());
}
String javaLangPackage = "java.lang.";
if (type.startsWith(javaLangPackage)) {
type = type.substring(javaLangPackage.length());
}
}
return nvl(type, "n/a");
}
private static String getConfigurationSource(JavaDocCapable<?> javaDocCapable) {
String source = getTag(javaDocCapable, "@configurationSource");
if ("{@link RepositorySystemSession#getConfigProperties()}".equals(source)) {
return "Session Configuration";
} else if ("{@link System#getProperty(String,String)}".equals(source)) {
return "Java System Properties";
} else if ("{@link org.apache.maven.api.model.Model#getProperties()}".equals(source)) {
return "Model Properties";
} else if ("{@link Session#getUserProperties()}".equals(source)) {
return "Session Properties";
} else {
return source;
}
}
private static String getSince(JavaDocCapable<?> javaDocCapable) {
List<JavaDocTag> tags;
if (javaDocCapable != null) {
if (javaDocCapable instanceof FieldSource<?> fieldSource) {
tags = fieldSource.getJavaDoc().getTags("@since");
if (tags.isEmpty()) {
return getSince(fieldSource.getOrigin());
} else {
return tags.get(0).getValue();
}
} else if (javaDocCapable instanceof JavaClassSource classSource) {
tags = classSource.getJavaDoc().getTags("@since");
if (!tags.isEmpty()) {
return tags.get(0).getValue();
}
}
}
return "";
}
private static String getTag(JavaDocCapable<?> javaDocCapable, String tagName) {
List<JavaDocTag> tags;
if (javaDocCapable != null) {
if (javaDocCapable instanceof FieldSource<?> fieldSource) {
tags = fieldSource.getJavaDoc().getTags(tagName);
if (tags.isEmpty()) {
return getTag(fieldSource.getOrigin(), tagName);
} else {
return tags.get(0).getValue();
}
}
}
return null;
}
private static final Pattern CONSTANT_PATTERN = Pattern.compile(".*static final.* ([A-Z_]+) = (.*);");
private static final ToolProvider JAVAP = ToolProvider.findFirst("javap").orElseThrow();
/**
* Builds "constant table" for one single class.
*
* Limitations:
* - works only for single class (no inherited constants)
* - does not work for fields that are Enum.name()
* - more to come
*/
private static Map<String, String> extractConstants(Path file) {
StringWriter out = new StringWriter();
JAVAP.run(new PrintWriter(out), new PrintWriter(System.err), "-constants", file.toString());
Map<String, String> result = new HashMap<>();
out.getBuffer().toString().lines().forEach(l -> {
Matcher matcher = CONSTANT_PATTERN.matcher(l);
if (matcher.matches()) {
result.put(matcher.group(1), matcher.group(2));
}
});
return result;
}
}

View File

@ -0,0 +1,55 @@
##
## Licensed to the Apache Software Foundation (ASF) under one
## or more contributor license agreements. See the NOTICE file
## distributed with this work for additional information
## regarding copyright ownership. The ASF licenses this file
## to you under the Apache License, Version 2.0 (the
## "License"); you may not use this file except in compliance
## with the License. You may obtain a copy of the License at
##
## http://www.apache.org/licenses/LICENSE-2.0
##
## Unless required by applicable law or agreed to in writing,
## software distributed under the License is distributed on an
## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
## KIND, either express or implied. See the License for the
## specific language governing permissions and limitations
## under the License.
##
#[[
# Configuration Options
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
]]#
#macro(yesno $val)
#if ($val) Yes #else No #end
#end
#macro(value $val)
#if ($val) `$val` #else - #end
#end
| No | Key | Type | Description | Default Value | Since | Source |
| --- | --- | --- | --- | --- | --- | --- |
#foreach($key in $keys)
| $foreach.count. | `$key.key` | `$key.configurationType` | $key.description | #value( $key.defaultValue ) | $key.since | $key.configurationSource |
#end

View File

@ -167,6 +167,12 @@ under the License.
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.jimfs</groupId>
<artifactId>jimfs</artifactId>
<version>1.3.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>

View File

@ -79,12 +79,18 @@ public class CLIManager {
public static final String ALTERNATE_PROJECT_SETTINGS = "ps";
@Deprecated
public static final String ALTERNATE_GLOBAL_SETTINGS = "gs";
public static final String ALTERNATE_INSTALLATION_SETTINGS = "is";
public static final char ALTERNATE_USER_TOOLCHAINS = 't';
@Deprecated
public static final String ALTERNATE_GLOBAL_TOOLCHAINS = "gt";
public static final String ALTERNATE_INSTALLATION_TOOLCHAINS = "it";
public static final String FAIL_FAST = "ff";
public static final String FAIL_ON_SEVERITY = "fos";
@ -222,6 +228,12 @@ public class CLIManager {
.desc("Alternate path for the global settings file")
.hasArg()
.build());
options.addOption(Option.builder(ALTERNATE_INSTALLATION_SETTINGS)
.longOpt("install-settings")
.desc("Alternate path for the installation settings file")
.hasArg()
.deprecated()
.build());
options.addOption(Option.builder(Character.toString(ALTERNATE_USER_TOOLCHAINS))
.longOpt("toolchains")
.desc("Alternate path for the user toolchains file")
@ -232,6 +244,12 @@ public class CLIManager {
.desc("Alternate path for the global toolchains file")
.hasArg()
.build());
options.addOption(Option.builder(ALTERNATE_INSTALLATION_TOOLCHAINS)
.longOpt("install-toolchains")
.desc("Alternate path for the installation toolchains file")
.hasArg()
.deprecated()
.build());
options.addOption(Option.builder(FAIL_ON_SEVERITY)
.longOpt("fail-on-severity")
.desc("Configure which severity of logging should cause the build to fail")

View File

@ -28,9 +28,10 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.nio.charset.Charset;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
@ -43,6 +44,7 @@ import java.util.Properties;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
@ -55,6 +57,7 @@ import org.apache.commons.cli.UnrecognizedOptionException;
import org.apache.maven.BuildAbort;
import org.apache.maven.InternalErrorException;
import org.apache.maven.Maven;
import org.apache.maven.api.Constants;
import org.apache.maven.api.services.MessageBuilder;
import org.apache.maven.api.services.MessageBuilderFactory;
import org.apache.maven.building.FileSource;
@ -71,6 +74,7 @@ import org.apache.maven.cli.logging.Slf4jConfiguration;
import org.apache.maven.cli.logging.Slf4jConfigurationFactory;
import org.apache.maven.cli.logging.Slf4jLoggerManager;
import org.apache.maven.cli.logging.Slf4jStdoutLogger;
import org.apache.maven.cli.props.MavenPropertiesLoader;
import org.apache.maven.cli.transfer.ConsoleMavenTransferListener;
import org.apache.maven.cli.transfer.QuietMavenTransferListener;
import org.apache.maven.cli.transfer.SimplexTransferListener;
@ -131,6 +135,8 @@ import org.sonatype.plexus.components.sec.dispatcher.SecUtil;
import org.sonatype.plexus.components.sec.dispatcher.model.SettingsSecurity;
import static java.util.Comparator.comparing;
import static org.apache.maven.api.Constants.MAVEN_HOME;
import static org.apache.maven.api.Constants.MAVEN_INSTALLATION_CONF;
import static org.apache.maven.cli.CLIManager.BATCH_MODE;
import static org.apache.maven.cli.CLIManager.COLOR;
import static org.apache.maven.cli.CLIManager.FORCE_INTERACTIVE;
@ -142,29 +148,11 @@ import static org.apache.maven.cli.ResolveFile.resolveFile;
/**
*/
public class MavenCli {
public static final String LOCAL_REPO_PROPERTY = "maven.repo.local";
public static final String MULTIMODULE_PROJECT_DIRECTORY = "maven.multiModuleProjectDirectory";
public static final String USER_HOME = System.getProperty("user.home");
public static final File USER_MAVEN_CONFIGURATION_HOME = new File(USER_HOME, ".m2");
public static final File DEFAULT_USER_TOOLCHAINS_FILE = new File(USER_MAVEN_CONFIGURATION_HOME, "toolchains.xml");
public static final File DEFAULT_GLOBAL_TOOLCHAINS_FILE =
new File(System.getProperty("maven.conf"), "toolchains.xml");
private static final String EXT_CLASS_PATH = "maven.ext.class.path";
private static final String EXTENSIONS_FILENAME = "extensions.xml";
private static final String MVN_EXTENSIONS_FILENAME = ".mvn/" + EXTENSIONS_FILENAME;
private static final String MVN_MAVEN_CONFIG = ".mvn/maven.config";
public static final String STYLE_COLOR_PROPERTY = "style.color";
private ClassWorld classWorld;
private LoggerManager plexusLoggerManager;
@ -191,6 +179,8 @@ public class MavenCli {
private MessageBuilderFactory messageBuilderFactory;
private FileSystem fileSystem = FileSystems.getDefault();
private static final Pattern NEXT_LINE = Pattern.compile("\r?\n");
public MavenCli() {
@ -340,7 +330,7 @@ public class MavenCli {
// We need to locate the top level project which may be pointed at using
// the -f/--file option. However, the command line isn't parsed yet, so
// we need to iterate through the args to find it and act upon it.
Path topDirectory = Paths.get(cliRequest.workingDirectory);
Path topDirectory = fileSystem.getPath(cliRequest.workingDirectory);
boolean isAltFile = false;
for (String arg : cliRequest.args) {
if (isAltFile) {
@ -380,10 +370,12 @@ public class MavenCli {
// Make sure the Maven home directory is an absolute path to save us from confusion with say drive-relative
// Windows paths.
//
String mavenHome = System.getProperty("maven.home");
String mavenHome = System.getProperty(Constants.MAVEN_HOME);
if (mavenHome != null) {
System.setProperty("maven.home", new File(mavenHome).getAbsolutePath());
System.setProperty(
Constants.MAVEN_HOME,
getCanonicalPath(fileSystem.getPath(mavenHome)).toString());
}
}
@ -510,7 +502,8 @@ public class MavenCli {
cliRequest.showErrors = cliRequest.verbose || commandLine.hasOption(CLIManager.ERRORS);
// LOG COLOR
String styleColor = cliRequest.getUserProperties().getProperty(STYLE_COLOR_PROPERTY, "auto");
String styleColor = cliRequest.getUserProperties().getProperty("style.color", "auto");
styleColor = cliRequest.getUserProperties().getProperty(Constants.MAVEN_STYLE_COLOR_PROPERTY, styleColor);
styleColor = commandLine.getOptionValue(COLOR, styleColor);
if ("always".equals(styleColor) || "yes".equals(styleColor) || "force".equals(styleColor)) {
MessageUtils.setColorEnabled(true);
@ -763,16 +756,17 @@ public class MavenCli {
return Collections.emptyList();
}
File extensionsFile = new File(cliRequest.multiModuleProjectDirectory, MVN_EXTENSIONS_FILENAME);
File userHomeExtensionsFile = new File(USER_MAVEN_CONFIGURATION_HOME, EXTENSIONS_FILENAME);
List<CoreExtension> extensions = new ArrayList<>();
if (extensionsFile.isFile()) {
extensions.addAll(readCoreExtensionsDescriptor(extensionsFile));
}
if (userHomeExtensionsFile.isFile()) {
extensions.addAll(readCoreExtensionsDescriptor(userHomeExtensionsFile));
}
String installationExtensionsFile =
cliRequest.getUserProperties().getProperty(Constants.MAVEN_INSTALLATION_EXTENSIONS);
extensions.addAll(readCoreExtensionsDescriptor(installationExtensionsFile));
String projectExtensionsFile = cliRequest.getUserProperties().getProperty(Constants.MAVEN_PROJECT_EXTENSIONS);
extensions.addAll(readCoreExtensionsDescriptor(projectExtensionsFile));
String userExtensionsFile = cliRequest.getUserProperties().getProperty(Constants.MAVEN_USER_EXTENSIONS);
extensions.addAll(readCoreExtensionsDescriptor(userExtensionsFile));
if (extensions.isEmpty()) {
return Collections.emptyList();
@ -824,13 +818,17 @@ public class MavenCli {
}
}
private List<CoreExtension> readCoreExtensionsDescriptor(File extensionsFile)
private List<CoreExtension> readCoreExtensionsDescriptor(String extensionsFile)
throws IOException, XMLStreamException {
CoreExtensionsStaxReader parser = new CoreExtensionsStaxReader();
try (InputStream is = Files.newInputStream(extensionsFile.toPath())) {
return parser.read(is, true).getExtensions();
if (extensionsFile != null) {
Path extensionsPath = Path.of(extensionsFile);
if (Files.exists(extensionsPath)) {
try (InputStream is = Files.newInputStream(extensionsPath)) {
return new CoreExtensionsStaxReader().read(is, true).getExtensions();
}
}
}
return List.of();
}
private ClassRealm setupContainerRealm(
@ -874,9 +872,16 @@ public class MavenCli {
}
private List<File> parseExtClasspath(CliRequest cliRequest) {
String extClassPath = cliRequest.userProperties.getProperty(EXT_CLASS_PATH);
String extClassPath = cliRequest.userProperties.getProperty(Constants.MAVEN_EXT_CLASS_PATH);
if (extClassPath == null) {
extClassPath = cliRequest.systemProperties.getProperty(EXT_CLASS_PATH);
extClassPath = cliRequest.systemProperties.getProperty(Constants.MAVEN_EXT_CLASS_PATH);
if (extClassPath != null) {
slf4jLogger.warn(
"The property '{}' has been set using a JVM system property which is deprecated. "
+ "The property can be passed as a Maven argument or in the Maven project configuration file,"
+ "usually located at ${session.rootDirectory}/.mvn/maven.properties.",
Constants.MAVEN_EXT_CLASS_PATH);
}
}
List<File> jars = new ArrayList<>();
@ -1193,7 +1198,7 @@ public class MavenCli {
}
void toolchains(CliRequest cliRequest) throws Exception {
File userToolchainsFile;
File userToolchainsFile = null;
if (cliRequest.commandLine.hasOption(CLIManager.ALTERNATE_USER_TOOLCHAINS)) {
userToolchainsFile = new File(cliRequest.commandLine.getOptionValue(CLIManager.ALTERNATE_USER_TOOLCHAINS));
@ -1204,40 +1209,57 @@ public class MavenCli {
"The specified user toolchains file does not exist: " + userToolchainsFile);
}
} else {
userToolchainsFile = DEFAULT_USER_TOOLCHAINS_FILE;
String userToolchainsFileStr = cliRequest.getUserProperties().getProperty(Constants.MAVEN_USER_TOOLCHAINS);
if (userToolchainsFileStr != null) {
userToolchainsFile = new File(userToolchainsFileStr);
}
}
File globalToolchainsFile;
File installationToolchainsFile = null;
if (cliRequest.commandLine.hasOption(CLIManager.ALTERNATE_GLOBAL_TOOLCHAINS)) {
globalToolchainsFile =
new File(cliRequest.commandLine.getOptionValue(CLIManager.ALTERNATE_GLOBAL_TOOLCHAINS));
globalToolchainsFile = resolveFile(globalToolchainsFile, cliRequest.workingDirectory);
if (cliRequest.commandLine.hasOption(CLIManager.ALTERNATE_INSTALLATION_TOOLCHAINS)) {
installationToolchainsFile =
new File(cliRequest.commandLine.getOptionValue(CLIManager.ALTERNATE_INSTALLATION_TOOLCHAINS));
installationToolchainsFile = resolveFile(installationToolchainsFile, cliRequest.workingDirectory);
if (!globalToolchainsFile.isFile()) {
if (!installationToolchainsFile.isFile()) {
throw new FileNotFoundException(
"The specified global toolchains file does not exist: " + globalToolchainsFile);
"The specified installation toolchains file does not exist: " + installationToolchainsFile);
}
} else if (cliRequest.commandLine.hasOption(CLIManager.ALTERNATE_GLOBAL_TOOLCHAINS)) {
installationToolchainsFile =
new File(cliRequest.commandLine.getOptionValue(CLIManager.ALTERNATE_GLOBAL_TOOLCHAINS));
installationToolchainsFile = resolveFile(installationToolchainsFile, cliRequest.workingDirectory);
if (!installationToolchainsFile.isFile()) {
throw new FileNotFoundException(
"The specified installation toolchains file does not exist: " + installationToolchainsFile);
}
} else {
globalToolchainsFile = DEFAULT_GLOBAL_TOOLCHAINS_FILE;
String installationToolchainsFileStr =
cliRequest.getUserProperties().getProperty(Constants.MAVEN_INSTALLATION_TOOLCHAINS);
if (installationToolchainsFileStr != null) {
installationToolchainsFile = new File(installationToolchainsFileStr);
installationToolchainsFile = resolveFile(installationToolchainsFile, cliRequest.workingDirectory);
}
}
cliRequest.request.setGlobalToolchainsFile(globalToolchainsFile);
cliRequest.request.setInstallationToolchainsFile(installationToolchainsFile);
cliRequest.request.setUserToolchainsFile(userToolchainsFile);
DefaultToolchainsBuildingRequest toolchainsRequest = new DefaultToolchainsBuildingRequest();
if (globalToolchainsFile.isFile()) {
toolchainsRequest.setGlobalToolchainsSource(new FileSource(globalToolchainsFile));
if (installationToolchainsFile != null && installationToolchainsFile.isFile()) {
toolchainsRequest.setGlobalToolchainsSource(new FileSource(installationToolchainsFile));
}
if (userToolchainsFile.isFile()) {
if (userToolchainsFile != null && userToolchainsFile.isFile()) {
toolchainsRequest.setUserToolchainsSource(new FileSource(userToolchainsFile));
}
eventSpyDispatcher.onEvent(toolchainsRequest);
slf4jLogger.debug(
"Reading global toolchains from '{}'",
getLocation(toolchainsRequest.getGlobalToolchainsSource(), globalToolchainsFile));
"Reading installation toolchains from '{}'",
getLocation(toolchainsRequest.getGlobalToolchainsSource(), installationToolchainsFile));
slf4jLogger.debug(
"Reading user toolchains from '{}'",
getLocation(toolchainsRequest.getUserToolchainsSource(), userToolchainsFile));
@ -1380,14 +1402,18 @@ public class MavenCli {
}
private String determineLocalRepositoryPath(final MavenExecutionRequest request) {
String userDefinedLocalRepo = request.getUserProperties().getProperty(MavenCli.LOCAL_REPO_PROPERTY);
if (userDefinedLocalRepo != null) {
return userDefinedLocalRepo;
String userDefinedLocalRepo = request.getUserProperties().getProperty(Constants.MAVEN_REPO_LOCAL);
if (userDefinedLocalRepo == null) {
userDefinedLocalRepo = request.getSystemProperties().getProperty(Constants.MAVEN_REPO_LOCAL);
if (userDefinedLocalRepo != null) {
slf4jLogger.warn(
"The property '{}' has been set using a JVM system property which is deprecated. "
+ "The property can be passed as a Maven argument or in the Maven project configuration file,"
+ "usually located at ${session.rootDirectory}/.mvn/maven.properties.",
Constants.MAVEN_REPO_LOCAL);
}
}
// TODO Investigate why this can also be a Java system property and not just a Maven user property like
// other properties
return request.getSystemProperties().getProperty(MavenCli.LOCAL_REPO_PROPERTY);
return userDefinedLocalRepo;
}
private File determinePom(final CommandLine commandLine, final String workingDirectory, final File baseDirectory) {
@ -1601,20 +1627,15 @@ public class MavenCli {
// Properties handling
// ----------------------------------------------------------------------
static void populateProperties(
void populateProperties(
CommandLine commandLine, Properties paths, Properties systemProperties, Properties userProperties)
throws Exception {
// ----------------------------------------------------------------------
// Load environment and system properties
// ----------------------------------------------------------------------
EnvironmentUtils.addEnvVars(systemProperties);
// ----------------------------------------------------------------------
// Options that are set on the command line become system properties
// and therefore are set in the session properties. System properties
// are most dominant.
// ----------------------------------------------------------------------
final Properties userSpecifiedProperties =
commandLine.getOptionProperties(String.valueOf(CLIManager.SET_USER_PROPERTY));
SystemProperties.addSystemProperties(systemProperties);
// ----------------------------------------------------------------------
@ -1630,20 +1651,65 @@ public class MavenCli {
String mavenBuildVersion = CLIReportingUtils.createMavenVersionString(buildProperties);
systemProperties.setProperty("maven.build.version", mavenBuildVersion);
BasicInterpolator interpolator =
createInterpolator(paths, systemProperties, userProperties, userSpecifiedProperties);
for (Map.Entry<Object, Object> e : userSpecifiedProperties.entrySet()) {
String name = (String) e.getKey();
String value = interpolator.interpolate((String) e.getValue());
userProperties.setProperty(name, value);
// ----------------------------------------------------------------------
// I'm leaving the setting of system properties here as not to break
// the SystemPropertyProfileActivator. This won't harm embedding. jvz.
// ----------------------------------------------------------------------
if (System.getProperty(name) == null) {
System.setProperty(name, value);
}
// ----------------------------------------------------------------------
// Options that are set on the command line become system properties
// and therefore are set in the session properties. System properties
// are most dominant.
// ----------------------------------------------------------------------
Properties userSpecifiedProperties =
commandLine.getOptionProperties(String.valueOf(CLIManager.SET_USER_PROPERTY));
userProperties.putAll(userSpecifiedProperties);
// ----------------------------------------------------------------------
// Load config files
// ----------------------------------------------------------------------
Function<String, String> callback =
or(paths::getProperty, prefix("cli.", commandLine::getOptionValue), systemProperties::getProperty);
Path mavenConf;
if (systemProperties.getProperty(MAVEN_INSTALLATION_CONF) != null) {
mavenConf = fileSystem.getPath(systemProperties.getProperty(MAVEN_INSTALLATION_CONF));
} else if (systemProperties.getProperty("maven.conf") != null) {
mavenConf = fileSystem.getPath(systemProperties.getProperty("maven.conf"));
} else if (systemProperties.getProperty(MAVEN_HOME) != null) {
mavenConf = fileSystem.getPath(systemProperties.getProperty(MAVEN_HOME), "conf");
} else {
mavenConf = fileSystem.getPath("");
}
Path propertiesFile = mavenConf.resolve("maven.properties");
MavenPropertiesLoader.loadProperties(userProperties, propertiesFile, callback, false);
// ----------------------------------------------------------------------
// I'm leaving the setting of system properties here as not to break
// the SystemPropertyProfileActivator. This won't harm embedding. jvz.
// ----------------------------------------------------------------------
Set<String> sys = SystemProperties.getSystemProperties().stringPropertyNames();
userProperties.stringPropertyNames().stream()
.filter(k -> !sys.contains(k))
.forEach(k -> System.setProperty(k, userProperties.getProperty(k)));
}
private static Function<String, String> prefix(String prefix, Function<String, String> cb) {
return s -> {
String v = null;
if (s.startsWith(prefix)) {
v = cb.apply(s.substring(prefix.length()));
}
return v;
};
}
private static Function<String, String> or(Function<String, String>... callbacks) {
return s -> {
for (Function<String, String> cb : callbacks) {
String r = cb.apply(s);
if (r != null) {
return r;
}
}
return null;
};
}
private static BasicInterpolator createInterpolator(Properties... properties) {
@ -1709,4 +1775,8 @@ public class MavenCli {
protected ModelProcessor createModelProcessor(PlexusContainer container) throws ComponentLookupException {
return container.lookup(ModelProcessor.class);
}
public void setFileSystem(FileSystem fileSystem) {
this.fileSystem = fileSystem;
}
}

View File

@ -69,7 +69,8 @@ public class SettingsXmlConfigurationProcessor implements ConfigurationProcessor
public static final File DEFAULT_PROJECT_SETTINGS_FILE = new File(".mvn", "settings.xml");
public static final File DEFAULT_GLOBAL_SETTINGS_FILE = new File(System.getProperty("maven.conf"), "settings.xml");
public static final File DEFAULT_INSTALLATION_SETTINGS_FILE =
new File(System.getProperty("maven.conf"), "settings.xml");
private static final Logger LOGGER = LoggerFactory.getLogger(SettingsXmlConfigurationProcessor.class);
@ -119,26 +120,34 @@ public class SettingsXmlConfigurationProcessor implements ConfigurationProcessor
projectSettingsFile = null;
}
File globalSettingsFile;
File installationSettingsFile;
if (commandLine.hasOption(CLIManager.ALTERNATE_GLOBAL_SETTINGS)) {
globalSettingsFile = new File(commandLine.getOptionValue(CLIManager.ALTERNATE_GLOBAL_SETTINGS));
globalSettingsFile = resolveFile(globalSettingsFile, workingDirectory);
if (commandLine.hasOption(CLIManager.ALTERNATE_INSTALLATION_SETTINGS)) {
installationSettingsFile = new File(commandLine.getOptionValue(CLIManager.ALTERNATE_INSTALLATION_SETTINGS));
installationSettingsFile = resolveFile(installationSettingsFile, workingDirectory);
if (!globalSettingsFile.isFile()) {
if (!installationSettingsFile.isFile()) {
throw new FileNotFoundException(
"The specified global settings file does not exist: " + globalSettingsFile);
"The specified installation settings file does not exist: " + installationSettingsFile);
}
} else if (commandLine.hasOption(CLIManager.ALTERNATE_GLOBAL_SETTINGS)) {
installationSettingsFile = new File(commandLine.getOptionValue(CLIManager.ALTERNATE_GLOBAL_SETTINGS));
installationSettingsFile = resolveFile(installationSettingsFile, workingDirectory);
if (!installationSettingsFile.isFile()) {
throw new FileNotFoundException(
"The specified installation settings file does not exist: " + installationSettingsFile);
}
} else {
globalSettingsFile = DEFAULT_GLOBAL_SETTINGS_FILE;
installationSettingsFile = DEFAULT_INSTALLATION_SETTINGS_FILE;
}
request.setGlobalSettingsFile(globalSettingsFile);
request.setInstallationSettingsFile(installationSettingsFile);
request.setProjectSettingsFile(projectSettingsFile);
request.setUserSettingsFile(userSettingsFile);
SettingsBuildingRequest settingsRequest = new DefaultSettingsBuildingRequest();
settingsRequest.setGlobalSettingsFile(globalSettingsFile);
settingsRequest.setGlobalSettingsFile(installationSettingsFile);
settingsRequest.setProjectSettingsFile(projectSettingsFile);
settingsRequest.setUserSettingsFile(userSettingsFile);
settingsRequest.setSystemProperties(cliRequest.getSystemProperties());
@ -155,7 +164,7 @@ public class SettingsXmlConfigurationProcessor implements ConfigurationProcessor
}
LOGGER.debug(
"Reading global settings from '{}'",
"Reading installation settings from '{}'",
getLocation(settingsRequest.getGlobalSettingsSource(), settingsRequest.getGlobalSettingsFile()));
LOGGER.debug(
"Reading project settings from '{}'",

View File

@ -0,0 +1,309 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.cli.props;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
public class InterpolationHelper {
private InterpolationHelper() {}
private static final char ESCAPE_CHAR = '\\';
private static final String DELIM_START = "${";
private static final String DELIM_STOP = "}";
private static final String MARKER = "$__";
/**
* Perform substitution on a property set
*
* @param properties the property set to perform substitution on
* @param callback Callback for substitution
*/
public static void performSubstitution(Map<String, String> properties, Function<String, String> callback) {
performSubstitution(properties, callback, true, true);
}
/**
* Perform substitution on a property set
*
* @param properties the property set to perform substitution on
* @param callback the callback to obtain substitution values
* @param substituteFromConfig If substitute from configuration
* @param defaultsToEmptyString sets an empty string if a replacement value is not found, leaves intact otherwise
*/
public static void performSubstitution(
Map<String, String> properties,
Function<String, String> callback,
boolean substituteFromConfig,
boolean defaultsToEmptyString) {
Map<String, String> org = new HashMap<>(properties);
for (String name : properties.keySet()) {
properties.compute(
name,
(k, value) ->
substVars(value, name, null, org, callback, substituteFromConfig, defaultsToEmptyString));
}
}
/**
* <p>
* This method performs property variable substitution on the
* specified value. If the specified value contains the syntax
* {@code ${&lt;prop-name&gt;}}, where {@code &lt;prop-name&gt;}
* refers to either a configuration property or a system property,
* then the corresponding property value is substituted for the variable
* placeholder. Multiple variable placeholders may exist in the
* specified value as well as nested variable placeholders, which
* are substituted from inner most to outer most. Configuration
* properties override system properties.
* </p>
*
* @param val The string on which to perform property substitution.
* @param currentKey The key of the property being evaluated used to
* detect cycles.
* @param cycleMap Map of variable references used to detect nested cycles.
* @param configProps Set of configuration properties.
* @return The value of the specified string after system property substitution.
* @throws IllegalArgumentException If there was a syntax error in the
* property placeholder syntax or a recursive variable reference.
**/
public static String substVars(
String val, String currentKey, Map<String, String> cycleMap, Map<String, String> configProps) {
return substVars(val, currentKey, cycleMap, configProps, null);
}
/**
* <p>
* This method performs property variable substitution on the
* specified value. If the specified value contains the syntax
* {@code ${&lt;prop-name&gt;}}, where {@code &lt;prop-name&gt;}
* refers to either a configuration property or a system property,
* then the corresponding property value is substituted for the variable
* placeholder. Multiple variable placeholders may exist in the
* specified value as well as nested variable placeholders, which
* are substituted from inner most to outer most. Configuration
* properties override system properties.
* </p>
*
* @param val The string on which to perform property substitution.
* @param currentKey The key of the property being evaluated used to
* detect cycles.
* @param cycleMap Map of variable references used to detect nested cycles.
* @param configProps Set of configuration properties.
* @param callback the callback to obtain substitution values
* @return The value of the specified string after system property substitution.
* @throws IllegalArgumentException If there was a syntax error in the
* property placeholder syntax or a recursive variable reference.
**/
public static String substVars(
String val,
String currentKey,
Map<String, String> cycleMap,
Map<String, String> configProps,
Function<String, String> callback) {
return substVars(val, currentKey, cycleMap, configProps, callback, true, false);
}
/**
* <p>
* This method performs property variable substitution on the
* specified value. If the specified value contains the syntax
* {@code ${&lt;prop-name&gt;}}, where {@code &lt;prop-name&gt;}
* refers to either a configuration property or a system property,
* then the corresponding property value is substituted for the variable
* placeholder. Multiple variable placeholders may exist in the
* specified value as well as nested variable placeholders, which
* are substituted from inner most to outer most. Configuration
* properties override system properties.
* </p>
*
* @param val The string on which to perform property substitution.
* @param currentKey The key of the property being evaluated used to
* detect cycles.
* @param cycleMap Map of variable references used to detect nested cycles.
* @param configProps Set of configuration properties.
* @param callback the callback to obtain substitution values
* @param substituteFromConfig If substitute from configuration
* @param defaultsToEmptyString sets an empty string if a replacement value is not found, leaves intact otherwise
* @return The value of the specified string after system property substitution.
* @throws IllegalArgumentException If there was a syntax error in the
* property placeholder syntax or a recursive variable reference.
**/
public static String substVars(
String val,
String currentKey,
Map<String, String> cycleMap,
Map<String, String> configProps,
Function<String, String> callback,
boolean substituteFromConfig,
boolean defaultsToEmptyString) {
return unescape(doSubstVars(
val, currentKey, cycleMap, configProps, callback, substituteFromConfig, defaultsToEmptyString));
}
private static String doSubstVars(
String val,
String currentKey,
Map<String, String> cycleMap,
Map<String, String> configProps,
Function<String, String> callback,
boolean substituteFromConfig,
boolean defaultsToEmptyString) {
if (cycleMap == null) {
cycleMap = new HashMap<>();
}
// Put the current key in the cycle map.
cycleMap.put(currentKey, currentKey);
// Assume we have a value that is something like:
// "leading ${foo.${bar}} middle ${baz} trailing"
// Find the first ending '}' variable delimiter, which
// will correspond to the first deepest nested variable
// placeholder.
int startDelim;
int stopDelim = -1;
do {
stopDelim = val.indexOf(DELIM_STOP, stopDelim + 1);
while (stopDelim > 0 && val.charAt(stopDelim - 1) == ESCAPE_CHAR) {
stopDelim = val.indexOf(DELIM_STOP, stopDelim + 1);
}
// Find the matching starting "${" variable delimiter
// by looping until we find a start delimiter that is
// greater than the stop delimiter we have found.
startDelim = val.indexOf(DELIM_START);
while (stopDelim >= 0) {
int idx = val.indexOf(DELIM_START, startDelim + DELIM_START.length());
if ((idx < 0) || (idx > stopDelim)) {
break;
} else if (idx < stopDelim) {
startDelim = idx;
}
}
} while (startDelim >= 0 && stopDelim >= 0 && stopDelim < startDelim + DELIM_START.length());
// If we do not have a start or stop delimiter, then just
// return the existing value.
if ((startDelim < 0) || (stopDelim < 0)) {
cycleMap.remove(currentKey);
return val;
}
// At this point, we have found a variable placeholder so
// we must perform a variable substitution on it.
// Using the start and stop delimiter indices, extract
// the first, deepest nested variable placeholder.
String variable = val.substring(startDelim + DELIM_START.length(), stopDelim);
String org = variable;
// Strip expansion modifiers
int idx1 = variable.lastIndexOf(":-");
int idx2 = variable.lastIndexOf(":+");
int idx = idx1 >= 0 && idx2 >= 0 ? Math.min(idx1, idx2) : idx1 >= 0 ? idx1 : idx2;
String op = null;
if (idx >= 0) {
op = variable.substring(idx);
variable = variable.substring(0, idx);
}
// Verify that this is not a recursive variable reference.
if (cycleMap.get(variable) != null) {
throw new IllegalArgumentException("recursive variable reference: " + variable);
}
String substValue = null;
// Get the value of the deepest nested variable placeholder.
// Try to configuration properties first.
if (substituteFromConfig && configProps != null) {
substValue = configProps.get(variable);
}
if (substValue == null) {
if (!variable.isEmpty()) {
if (callback != null) {
substValue = callback.apply(variable);
}
}
}
if (op != null) {
if (op.startsWith(":-")) {
if (substValue == null || substValue.isEmpty()) {
substValue = op.substring(":-".length());
}
} else if (op.startsWith(":+")) {
if (substValue != null && !substValue.isEmpty()) {
substValue = op.substring(":+".length());
}
} else {
throw new IllegalArgumentException("Bad substitution: ${" + org + "}");
}
}
if (substValue == null) {
if (defaultsToEmptyString) {
substValue = "";
} else {
// alters the original token to avoid infinite recursion
// altered tokens are reverted in substVarsPreserveUnresolved()
substValue = MARKER + "{" + variable + "}";
}
}
// Remove the found variable from the cycle map, since
// it may appear more than once in the value and we don't
// want such situations to appear as a recursive reference.
cycleMap.remove(variable);
// Append the leading characters, the substituted value of
// the variable, and the trailing characters to get the new
// value.
val = val.substring(0, startDelim) + substValue + val.substring(stopDelim + DELIM_STOP.length());
// Now perform substitution again, since there could still
// be substitutions to make.
val = doSubstVars(
val, currentKey, cycleMap, configProps, callback, substituteFromConfig, defaultsToEmptyString);
cycleMap.remove(currentKey);
// Return the value.
return val;
}
public static String escape(String val) {
return val.replace("$", MARKER);
}
private static String unescape(String val) {
val = val.replaceAll("\\" + MARKER, "\\$");
int escape = val.indexOf(ESCAPE_CHAR);
while (escape >= 0 && escape < val.length() - 1) {
char c = val.charAt(escape + 1);
if (c == '{' || c == '}' || c == ESCAPE_CHAR) {
val = val.substring(0, escape) + val.substring(escape + 1);
}
escape = val.indexOf(ESCAPE_CHAR, escape + 1);
}
return val;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,161 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.cli.props;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.function.Function;
import static org.apache.maven.cli.props.InterpolationHelper.substVars;
public class MavenPropertiesLoader {
public static final String INCLUDES_PROPERTY = "${includes}"; // includes
public static final String OVERRIDE_PREFIX =
"maven.override."; // prefix that marks that system property should override defaults.
public static void loadProperties(
java.util.Properties properties, Path path, Function<String, String> callback, boolean escape)
throws IOException {
MavenProperties sp = new MavenProperties(false);
if (Files.exists(path)) {
sp.load(path);
}
properties.forEach(
(k, v) -> sp.put(k.toString(), escape ? InterpolationHelper.escape(v.toString()) : v.toString()));
loadIncludes(path, sp, callback);
substitute(sp, callback);
sp.forEach(properties::setProperty);
}
public static void substitute(MavenProperties props, Function<String, String> callback) {
for (Enumeration<?> e = props.propertyNames(); e.hasMoreElements(); ) {
String name = (String) e.nextElement();
String value = props.getProperty(name);
if (value == null) {
value = callback.apply(name);
}
if (name.startsWith(OVERRIDE_PREFIX)) {
String overrideName = name.substring(OVERRIDE_PREFIX.length());
props.put(overrideName, substVars(value, name, null, props, callback));
} else {
props.put(name, substVars(value, name, null, props, callback));
}
}
props.keySet().removeIf(k -> k.startsWith(OVERRIDE_PREFIX));
}
private static MavenProperties loadPropertiesFile(
Path path, boolean failIfNotFound, Function<String, String> callback) throws IOException {
MavenProperties configProps = new MavenProperties(null, false);
if (Files.exists(path) || failIfNotFound) {
configProps.load(path);
loadIncludes(path, configProps, callback);
trimValues(configProps);
}
return configProps;
}
private static void loadIncludes(Path configProp, MavenProperties configProps, Function<String, String> callback)
throws IOException {
String includes = configProps.get(INCLUDES_PROPERTY);
if (includes != null) {
includes = substVars(includes, INCLUDES_PROPERTY, null, configProps, callback);
StringTokenizer st = new StringTokenizer(includes, "?\",", true);
if (st.countTokens() > 0) {
String location;
do {
location = nextLocation(st);
if (location != null) {
boolean mandatory = true;
if (location.startsWith("?")) {
mandatory = false;
location = location.substring(1);
}
Path path = configProp.resolveSibling(location);
MavenProperties props = loadPropertiesFile(path, mandatory, s -> {
var v = callback.apply(s);
return v != null ? v : configProps.getProperty(s);
});
configProps.putAll(props);
}
} while (location != null);
}
}
configProps.remove(INCLUDES_PROPERTY);
}
private static void trimValues(MavenProperties configProps) {
configProps.replaceAll((k, v) -> v.trim());
}
private static String nextLocation(StringTokenizer st) {
boolean optional = false;
String retVal = null;
if (st.countTokens() > 0) {
String tokenList = "?\",";
StringBuilder tokBuf = new StringBuilder(10);
String tok;
boolean inQuote = false;
boolean tokStarted = false;
boolean exit = false;
while ((st.hasMoreTokens()) && (!exit)) {
tok = st.nextToken(tokenList);
switch (tok) {
case "\"":
inQuote = !inQuote;
if (inQuote) {
tokenList = "\"";
} else {
tokenList = "\" ";
}
break;
case ",":
if (tokStarted) {
retVal = tokBuf.toString();
tokStarted = false;
tokBuf = new StringBuilder(10);
exit = true;
}
break;
case "?":
optional = true;
break;
default:
tokStarted = true;
tokBuf.append(tok.trim());
break;
}
}
// Handle case where end of token stream and
// still got data
if ((!exit) && (tokStarted)) {
retVal = tokBuf.toString();
}
}
return optional ? "?" + retVal : retVal;
}
}

View File

@ -22,12 +22,16 @@ import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;
import com.google.common.jimfs.Configuration;
import com.google.common.jimfs.Jimfs;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
@ -35,6 +39,7 @@ import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.maven.Maven;
import org.apache.maven.api.Constants;
import org.apache.maven.cli.transfer.ConsoleMavenTransferListener;
import org.apache.maven.cli.transfer.QuietMavenTransferListener;
import org.apache.maven.cli.transfer.SimplexTransferListener;
@ -51,6 +56,7 @@ import org.apache.maven.toolchain.building.ToolchainsBuildingResult;
import org.codehaus.plexus.DefaultPlexusContainer;
import org.codehaus.plexus.PlexusContainer;
import org.eclipse.aether.transfer.TransferListener;
import org.hamcrest.CoreMatchers;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -453,24 +459,25 @@ class MavenCliTest {
}
@Test
void verifyLocalRepositoryPath() {
void verifyLocalRepositoryPath() throws Exception {
MavenCli cli = new MavenCli();
CliRequest request = new CliRequest(new String[] {}, null);
request.commandLine = new CommandLine.Builder().build();
MavenExecutionRequest executionRequest;
// Use default
cli.cli(request);
executionRequest = cli.populateRequest(request);
assertThat(executionRequest.getLocalRepositoryPath(), is(nullValue()));
// System-properties override default
request.getSystemProperties().setProperty(MavenCli.LOCAL_REPO_PROPERTY, "." + File.separatorChar + "custom1");
request.getSystemProperties().setProperty(Constants.MAVEN_REPO_LOCAL, "." + File.separatorChar + "custom1");
executionRequest = cli.populateRequest(request);
assertThat(executionRequest.getLocalRepositoryPath(), is(notNullValue()));
assertThat(executionRequest.getLocalRepositoryPath().toString(), is("." + File.separatorChar + "custom1"));
// User-properties override system properties
request.getUserProperties().setProperty(MavenCli.LOCAL_REPO_PROPERTY, "." + File.separatorChar + "custom2");
request.getUserProperties().setProperty(Constants.MAVEN_REPO_LOCAL, "." + File.separatorChar + "custom2");
executionRequest = cli.populateRequest(request);
assertThat(executionRequest.getLocalRepositoryPath(), is(notNullValue()));
assertThat(executionRequest.getLocalRepositoryPath().toString(), is("." + File.separatorChar + "custom2"));
@ -578,9 +585,27 @@ class MavenCliTest {
@Test
public void testPropertiesInterpolation() throws Exception {
FileSystem fs = Jimfs.newFileSystem(Configuration.windows());
Path mavenHome = fs.getPath("C:\\maven");
Files.createDirectories(mavenHome);
Path mavenConf = mavenHome.resolve("conf");
Files.createDirectories(mavenConf);
Path mavenUserProps = mavenConf.resolve("maven.properties");
Files.writeString(mavenUserProps, "${includes} = ?${session.rootDirectory}/.mvn/maven.properties\n");
Path rootDirectory = fs.getPath("C:\\myRootDirectory");
Path topDirectory = rootDirectory.resolve("myTopDirectory");
Path mvn = rootDirectory.resolve(".mvn");
Files.createDirectories(mvn);
Files.writeString(
mvn.resolve("maven.properties"),
"${includes} = env-${envName}.properties\nfro = ${bar}z\n" + "bar = chti${java.version}\n");
Files.writeString(mvn.resolve("env-test.properties"), "\n");
// Arrange
CliRequest request = new CliRequest(
new String[] {
"-DenvName=test",
"-Dfoo=bar",
"-DvalFound=s${foo}i",
"-DvalNotFound=s${foz}i",
@ -592,20 +617,28 @@ class MavenCliTest {
"validate"
},
null);
request.rootDirectory = Paths.get("myRootDirectory");
request.topDirectory = Paths.get("myTopDirectory");
request.rootDirectory = rootDirectory;
request.topDirectory = topDirectory;
System.setProperty("maven.installation.conf", mavenConf.toString());
// Act
cli.setFileSystem(fs);
cli.cli(request);
cli.properties(request);
// Assert
assertThat(request.getUserProperties().getProperty("fro"), CoreMatchers.startsWith("chti"));
assertThat(request.getUserProperties().getProperty("valFound"), is("sbari"));
assertThat(request.getUserProperties().getProperty("valNotFound"), is("s${foz}i"));
assertThat(request.getUserProperties().getProperty("valRootDirectory"), is("myRootDirectory/.mvn/foo"));
assertThat(request.getUserProperties().getProperty("valTopDirectory"), is("myTopDirectory/pom.xml"));
assertThat(request.getCommandLine().getOptionValue('f'), is("myRootDirectory/my-child"));
assertThat(request.getUserProperties().getProperty("valRootDirectory"), is("C:\\myRootDirectory/.mvn/foo"));
assertThat(
request.getUserProperties().getProperty("valTopDirectory"),
is("C:\\myRootDirectory\\myTopDirectory/pom.xml"));
assertThat(request.getCommandLine().getOptionValue('f'), is("C:\\myRootDirectory/my-child"));
assertThat(request.getCommandLine().getArgs(), equalTo(new String[] {"prefix:3.0.0:bar", "validate"}));
Path p = fs.getPath(request.getUserProperties().getProperty("valTopDirectory"));
assertThat(p.toString(), is("C:\\myRootDirectory\\myTopDirectory\\pom.xml"));
}
@Test

View File

@ -0,0 +1,95 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.cli.props;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.util.Properties;
import com.google.common.jimfs.Configuration;
import com.google.common.jimfs.Jimfs;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
class MavenPropertiesLoaderTest {
@Test
void testIncludes() throws Exception {
FileSystem fs = Jimfs.newFileSystem(Configuration.unix());
Path mavenHome = fs.getPath("/maven");
Files.createDirectories(mavenHome);
Path mavenConf = mavenHome.resolve("conf");
Files.createDirectories(mavenConf);
Path mavenUserProps = mavenConf.resolve("maven.properties");
Files.writeString(mavenUserProps, "${includes} = ?\"/user/ma ven.properties\", ?/foo/bar\n");
Path userDirectory = fs.getPath("/user");
Files.createDirectories(userDirectory);
Path propsPath = userDirectory.resolve("ma ven.properties");
Files.writeString(propsPath, "${includes} = another.properties\nfro = ${bar}z\n");
Properties p = new Properties();
p.put("java.version", "11");
assertThrows(
NoSuchFileException.class, () -> MavenPropertiesLoader.loadProperties(p, mavenUserProps, null, false));
Path another = propsPath.resolveSibling("another.properties");
Files.writeString(another, "bar = chti${java.version}\n");
MavenPropertiesLoader.loadProperties(p, mavenUserProps, null, false);
assertEquals("chti11z", p.getProperty("fro"));
}
@Test
void testIncludes3() throws Exception {
FileSystem fs = Jimfs.newFileSystem(Configuration.unix());
Path mavenHome = fs.getPath("/maven");
Files.createDirectories(mavenHome);
Path mavenConf = mavenHome.resolve("conf");
Files.createDirectories(mavenConf);
Path mavenUserProps = mavenConf.resolve("maven.properties");
Files.writeString(mavenUserProps, "${includes} = ?\"${user.home}/maven.properties\"\n");
Path userDirectory = fs.getPath("/user");
Files.createDirectories(userDirectory);
Path propsPath = userDirectory.resolve("maven.properties");
Files.writeString(propsPath, "${includes} = default.properties,?env-${env.envName}.properties\n");
Path defPath = userDirectory.resolve("default.properties");
Files.writeString(defPath, "foo=bar");
Path envPath = userDirectory.resolve("env-ci.properties");
Files.writeString(envPath, "foo=bar-env\nfoo-env=bar\n");
Properties p = new Properties();
p.put("user.home", userDirectory.toString());
MavenPropertiesLoader.loadProperties(p, mavenUserProps, p::getProperty, false);
assertEquals("bar", p.getProperty("foo"));
assertNull(p.getProperty("foo-env"));
p = new Properties();
p.put("user.home", userDirectory.toString());
p.put("env.envName", "ci");
MavenPropertiesLoader.loadProperties(p, mavenUserProps, p::getProperty, false);
assertEquals("bar-env", p.getProperty("foo"));
assertEquals("bar", p.getProperty("foo-env"));
}
}

View File

@ -0,0 +1,428 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.cli.props;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* Unit tests on <code>MavenProperties</code>.
*/
public class MavenPropertiesTest {
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
private static final String COMMENT = "# comment";
private static final String KEY1 = "mvn:foo/bar";
private static final String KEY1A = "mvn\\:foo/bar";
private static final String KEY2 = "foo:bar:version:type:classifier";
private static final String KEY2A = "foo\\:bar\\:version\\:type\\:classifier";
private static final String VALUE1 = "value";
private MavenProperties properties;
static final String TEST_PROPERTIES =
"""
#
# test.properties
# Used in the PropertiesTest
#
test=test
""";
/*
* (non-Javadoc)
* @see junit.framework.TestCase#setUp()
*/
@BeforeEach
public void setUp() throws Exception {
properties = new MavenProperties();
properties.load(new StringReader(TEST_PROPERTIES));
}
@Test
public void testSpaces() throws Exception {
String config = "\n" + "\n"
+ " \n"
+ " \n"
+ " \\ \\r \\n \\t \\f\n"
+ " \n"
+ " \n"
+ "! dshfjklahfjkldashgjl;as\n"
+ " #jdfagdfjagkdjfghksdajfd\n"
+ " \n"
+ "!!properties\n"
+ "\n"
+ "a=a\n"
+ "b bb as,dn \n"
+ "c\\r\\ \\t\\nu =:: cu\n"
+ "bu= b\\\n"
+ " u\n"
+ "d=d\\r\\ne=e\n"
+ "f :f\\\n"
+ "f\\\n"
+ " f\n"
+ "g g\n"
+ "h\\u0020h\n"
+ "\\ i=i\n"
+ "j=\\ j\n"
+ "space=\\ c\n"
+ "\n"
+ "dblbackslash=\\\\\n"
+ " \n";
java.util.Properties props1 = new java.util.Properties();
props1.load(new StringReader(config));
MavenProperties props2 = new MavenProperties();
props2.load(new StringReader(config));
String s325 = props1.getProperty(" \r");
assertEquals("\n \t \f", s325, "1");
String s324 = props1.getProperty("a");
assertEquals("a", s324, "2");
String s323 = props1.getProperty("b");
assertEquals("bb as,dn ", s323, "3");
String s322 = props1.getProperty("c\r \t\nu");
assertEquals(":: cu", s322, "4");
String s321 = props1.getProperty("bu");
assertEquals("bu", s321, "5");
String s320 = props1.getProperty("d");
assertEquals("d\r\ne=e", s320, "6");
String s319 = props1.getProperty("f");
assertEquals("fff", s319, "7");
String s318 = props1.getProperty("g");
assertEquals("g", s318, "8");
String s317 = props1.getProperty("h h");
assertEquals("", s317, "9");
String s316 = props1.getProperty(" ");
assertEquals("i=i", s316, "10");
String s315 = props1.getProperty("j");
assertEquals(" j", s315, "11");
String s314 = props1.getProperty("space");
assertEquals(" c", s314, "12");
String s313 = props1.getProperty("dblbackslash");
assertEquals("\\", s313, "13");
String s312 = props2.getProperty(" \r");
assertEquals("\n \t \f", s312, "1");
String s311 = props2.getProperty("a");
assertEquals("a", s311, "2");
String s310 = props2.getProperty("b");
assertEquals("bb as,dn ", s310, "3");
String s39 = props2.getProperty("c\r \t\nu");
assertEquals(":: cu", s39, "4");
String s38 = props2.getProperty("bu");
assertEquals("bu", s38, "5");
String s37 = props2.getProperty("d");
assertEquals("d\r\ne=e", s37, "6");
String s36 = props2.getProperty("f");
assertEquals("fff", s36, "7");
String s35 = props2.getProperty("g");
assertEquals("g", s35, "8");
String s34 = props2.getProperty("h h");
assertEquals("", s34, "9");
String s33 = props2.getProperty(" ");
assertEquals("i=i", s33, "10");
String s32 = props2.getProperty("j");
assertEquals(" j", s32, "11");
String s31 = props2.getProperty("space");
assertEquals(" c", s31, "12");
String s3 = props2.getProperty("dblbackslash");
assertEquals("\\", s3, "13");
assertEquals(props1, props2);
}
@Test
public void testConfigInterpolation() throws IOException {
String config = "a=$\\\\\\\\{var}\n" + "ab=${a}b\n" + "abc=${ab}c";
java.util.Properties props1 = new java.util.Properties();
props1.load(new StringReader(config));
InterpolationHelper.performSubstitution((Map) props1, null);
MavenProperties props2 = new MavenProperties();
props2.load(new StringReader(config));
assertEquals(props1, props2);
}
/**
* <p>
* Test getting property.
* </p>
*
* @throws Exception
*/
@Test
public void testGettingProperty() throws Exception {
Object o2 = properties.get("test");
assertEquals("test", o2);
}
@Test
public void testLoadSave() throws IOException {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
pw.println("# ");
pw.println("# The Main ");
pw.println("# ");
pw.println("# Comment ");
pw.println("# ");
pw.println("");
pw.println("# Another comment");
pw.println("");
pw.println("# A value comment");
pw.println("key1 = val1");
pw.println("");
pw.println("# Another value comment");
pw.println("key2 = ${key1}/foo");
pw.println("");
pw.println("# A third comment");
pw.println("key3 = val3");
pw.println("");
MavenProperties props = new MavenProperties();
props.load(new StringReader(sw.toString()));
props.save(System.err);
System.err.println("=====");
props.put("key2", props.get("key2"));
props.put("key3", "foo");
props.save(System.err);
System.err.println("=====");
}
@Test
public void testJavaUtilPropertiesCompatibility() throws Exception {
MavenProperties properties = new MavenProperties();
properties.load(new StringReader(TEST_PROPERTIES));
String test = properties.getProperty("test");
assertEquals(test, "test");
String defaultValue = properties.getProperty("notfound", "default");
assertEquals(defaultValue, "default");
properties.setProperty("another", "another");
Object o1 = properties.getProperty("another");
assertEquals(o1, "another");
properties.store(System.err, null);
System.err.println("====");
}
private static final String RESULT1 = COMMENT + LINE_SEPARATOR + KEY1A + " = " + VALUE1 + LINE_SEPARATOR;
@Test
public void testSaveComment1() throws Exception {
properties.put(KEY1, COMMENT, VALUE1);
StringWriter sw = new StringWriter();
properties.save(sw);
String msg = sw.toString();
assertTrue(sw.toString().endsWith(RESULT1), msg);
}
private static final String RESULT1A = COMMENT + LINE_SEPARATOR + KEY2A + " = " + VALUE1 + LINE_SEPARATOR;
@Test
public void testSaveComment1a() throws Exception {
properties.put(KEY2, COMMENT, VALUE1);
StringWriter sw = new StringWriter();
properties.save(sw);
String msg = sw.toString();
assertTrue(sw.toString().endsWith(RESULT1A), msg);
}
private static final String RESULT2 =
COMMENT + LINE_SEPARATOR + COMMENT + LINE_SEPARATOR + KEY1A + " = " + VALUE1 + LINE_SEPARATOR;
@Test
public void testSaveComment2() throws Exception {
properties.put(KEY1, List.of(new String[] {COMMENT, COMMENT}), VALUE1);
StringWriter sw = new StringWriter();
properties.save(sw);
String msg = sw.toString();
assertTrue(sw.toString().endsWith(RESULT2), msg);
}
private static final String RESULT3 = COMMENT + LINE_SEPARATOR + COMMENT + LINE_SEPARATOR + KEY1A + " = " + VALUE1
+ "\\" + LINE_SEPARATOR + VALUE1 + LINE_SEPARATOR;
@Test
public void testSaveComment3() throws Exception {
properties.put(KEY1, List.of(new String[] {COMMENT, COMMENT}), List.of(new String[] {VALUE1, VALUE1}));
StringWriter sw = new StringWriter();
properties.save(sw);
String msg = sw.toString();
assertTrue(sw.toString().endsWith(RESULT3), msg);
List<String> rawValue = properties.getRaw(KEY1);
assertEquals(2, (Object) rawValue.size());
assertEquals(KEY1A + " = " + VALUE1, rawValue.get(0));
assertEquals(VALUE1, rawValue.get(1));
}
@Test
public void testEntrySetValue() throws Exception {
properties.put(KEY1, VALUE1);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
properties.save(baos);
properties = new MavenProperties();
properties.load(new ByteArrayInputStream(baos.toByteArray()));
Object o22 = properties.get(KEY1);
assertEquals(VALUE1, o22);
for (Map.Entry<String, String> entry : properties.entrySet()) {
entry.setValue(entry.getValue() + "x");
}
Object o21 = properties.get(KEY1);
assertEquals(VALUE1 + "x", o21);
baos = new ByteArrayOutputStream();
properties.save(baos);
properties = new MavenProperties();
properties.load(new ByteArrayInputStream(baos.toByteArray()));
Object o2 = properties.get(KEY1);
assertEquals(VALUE1 + "x", o2);
}
@Test
public void testMultiValueEscaping() throws IOException {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
pw.println("fruits apple, banana, pear, \\");
pw.println(" cantaloupe, watermelon, \\");
pw.println(" kiwi, mango");
java.util.Properties p = new java.util.Properties();
p.load(new StringReader(sw.toString()));
Object o24 = p.getProperty("fruits");
assertEquals("apple, banana, pear, cantaloupe, watermelon, kiwi, mango", o24);
MavenProperties props = new MavenProperties();
props.load(new StringReader(sw.toString()));
Object o23 = props.getProperty("fruits");
assertEquals("apple, banana, pear, cantaloupe, watermelon, kiwi, mango", o23);
List<String> raw = props.getRaw("fruits");
assertNotNull(raw);
assertEquals(3, (Object) raw.size());
assertEquals("fruits apple, banana, pear, ", raw.get(0));
props = new MavenProperties();
props.put(
"fruits",
props.getComments("fruits"),
List.of(
"fruits apple, banana, pear, ",
" cantaloupe, watermelon, ",
" kiwi, mango"));
Object o22 = props.getProperty("fruits");
assertEquals("apple, banana, pear, cantaloupe, watermelon, kiwi, mango", o22);
raw = props.getRaw("fruits");
assertNotNull(raw);
assertEquals(3, (Object) raw.size());
assertEquals("fruits apple, banana, pear, ", raw.get(0));
sw = new StringWriter();
props.save(sw);
props = new MavenProperties();
props.load(new StringReader(sw.toString()));
Object o21 = props.getProperty("fruits");
assertEquals("apple, banana, pear, cantaloupe, watermelon, kiwi, mango", o21);
raw = props.getRaw("fruits");
assertNotNull(raw);
assertEquals(3, (Object) raw.size());
assertEquals("fruits apple, banana, pear, ", raw.get(0));
props = new MavenProperties();
props.put(
"fruits",
props.getComments("fruits"),
List.of(
" apple, banana, pear, ",
" cantaloupe, watermelon, ",
" kiwi, mango"));
Object o2 = props.getProperty("fruits");
assertEquals("apple, banana, pear, cantaloupe, watermelon, kiwi, mango", o2);
raw = props.getRaw("fruits");
assertNotNull(raw);
assertEquals(3, (Object) raw.size());
assertEquals("fruits = apple, banana, pear, ", raw.get(0));
}
@Test
public void testUpdate() throws Exception {
MavenProperties p1 = new MavenProperties();
p1.put(
"fruits",
List.of("#", "# List of fruits", "#"),
List.of(
" apple, banana, pear, ",
" cantaloupe, watermelon, ",
" kiwi, mango"));
p1.put("trees", List.of("#", "# List of trees", "#"), List.of(" fir, oak, maple"));
p1.put("vegetables", List.of("#", "# List of vegetables", "#"), List.of(" potatoes"));
MavenProperties p2 = new MavenProperties();
p2.put(
"fruits",
List.of("#", "# List of good fruits", "#"),
List.of(" apple, banana, pear"));
p2.put("trees", "fir, oak, maple");
p1.update(p2);
assertEquals(2, (Object) p1.size());
Object o23 = p1.getComments("trees");
assertEquals(List.of("#", "# List of trees", "#"), o23);
Object o22 = p1.getProperty("trees");
assertEquals("fir, oak, maple", o22);
Object o21 = p1.getComments("fruits");
assertEquals(List.of("#", "# List of good fruits", "#"), o21);
Object o2 = p1.getProperty("fruits");
assertEquals("apple, banana, pear", o2);
}
@Test
public void testSubstitution() throws IOException {
String str = "port = 4141" + LINE_SEPARATOR + "host = localhost"
+ LINE_SEPARATOR + "url = https://${host}:${port}/service"
+ LINE_SEPARATOR;
MavenProperties properties = new MavenProperties();
properties.load(new StringReader(str));
properties.put("url", "https://localhost:4141/service");
StringWriter sw = new StringWriter();
properties.save(sw);
Object o2 = sw.toString();
assertEquals(str, o2);
}
}

View File

@ -85,7 +85,8 @@ public class DefaultSettingsBuilder implements SettingsBuilder {
}
return null;
}))
.globalSettingsSource(toSource(request.getGlobalSettingsFile(), request.getGlobalSettingsSource()))
.installationSettingsSource(
toSource(request.getGlobalSettingsFile(), request.getGlobalSettingsSource()))
.projectSettingsSource(
toSource(request.getProjectSettingsFile(), request.getProjectSettingsSource()))
.userSettingsSource(toSource(request.getUserSettingsFile(), request.getUserSettingsSource()))

View File

@ -85,7 +85,7 @@ public class DefaultToolchainsBuilder implements ToolchainsBuilder {
}
return null;
}))
.globalToolchainsSource(convert(request.getGlobalToolchainsSource()))
.installationToolchainsSource(convert(request.getGlobalToolchainsSource()))
.userToolchainsSource(convert(request.getUserToolchainsSource()))
.build());

View File

@ -123,6 +123,7 @@ under the License.
<module>maven-toolchain-model</module>
<module>maven-toolchain-builder</module>
<module>maven-bom</module>
<module>maven-docgen</module>
</modules>
<scm>

View File

@ -0,0 +1,53 @@
# Configuration Options
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
| No | Key | Type | Description | Default Value | Since | Source |
| --- | --- | --- | --- | --- | --- | --- |
| 1. | `maven.build.timestamp.format` | `String` | Build timestamp format. | `yyyy-MM-dd'T'HH:mm:ssXXX` | 3.0.0 | Model properties |
| 2. | `maven.ext.class.path` | `String` | Extensions class path. | - | | User properties |
| 3. | `maven.home` | `String` | Maven home. | - | 3.0.0 | User properties |
| 4. | `maven.installation.conf` | `String` | Maven installation configuration directory. | `${maven.home}/conf` | 4.0.0 | User properties |
| 5. | `maven.installation.extensions` | `String` | Maven installation extensions. | `${maven.installation.conf}/extensions.xml` | 4.0.0 | User properties |
| 6. | `maven.installation.settings` | `String` | Maven installation settings. | `${maven.installation.conf}/settings.xml` | 4.0.0 | User properties |
| 7. | `maven.installation.toolchains` | `String` | Maven installation toolchains. | `${maven.installation.conf}/toolchains.xml` | 4.0.0 | User properties |
| 8. | `maven.plugin.validation` | `String` | Plugin validation level. | `inline` | 3.9.2 | User properties |
| 9. | `maven.plugin.validation.excludes` | `String` | Plugin validation exclusions. | - | 3.9.6 | User properties |
| 10. | `maven.project.conf` | `String` | Maven project configuration directory. | `${session.rootDirectory}/.mvn` | 4.0.0 | User properties |
| 11. | `maven.project.extensions` | `String` | Maven project extensions. | `${maven.project.conf}/extensions.xml` | 4.0.0 | User properties |
| 12. | `maven.project.settings` | `String` | Maven project settings. | `${maven.project.conf}/settings.xml` | 4.0.0 | User properties |
| 13. | `maven.projectBuilder.parallelism` | `Integer` | ProjectBuilder parallelism. | `cores/2 + 1` | 4.0.0 | User properties |
| 14. | `maven.relocations.entries` | `String` | User controlled relocations. This property is a comma separated list of entries with the syntax <code>GAV&gt;GAV</code>. The first <code>GAV</code> can contain <code>\*</code> for any elem (so <code>\*:\*:\*</code> would mean ALL, something you don't want). The second <code>GAV</code> is either fully specified, or also can contain <code>\*</code>, then it behaves as "ordinary relocation": the coordinate is preserved from relocated artifact. Finally, if right hand <code>GAV</code> is absent (line looks like <code>GAV&gt;</code>), the left hand matching <code>GAV</code> is banned fully (from resolving). <br/> Note: the <code>&gt;</code> means project level, while <code>&gt;&gt;</code> means global (whole session level, so even plugins will get relocated artifacts) relocation. <br/> For example, <pre>maven.relocations.entries = org.foo:\*:\*>, \\<br/> org.here:\*:\*>org.there:\*:\*, \\<br/> javax.inject:javax.inject:1>>jakarta.inject:jakarta.inject:1.0.5</pre> means: 3 entries, ban <code>org.foo group</code> (exactly, so <code>org.foo.bar</code> is allowed), relocate <code>org.here</code> to <code>org.there</code> and finally globally relocate (see <code>&gt;&gt;</code> above) <code>javax.inject:javax.inject:1</code> to <code>jakarta.inject:jakarta.inject:1.0.5</code>. | - | 4.0.0 | User properties |
| 15. | `maven.repo.local` | `String` | Maven local repository. | `${maven.user.conf}/repository` | 3.0.0 | User properties |
| 16. | `maven.repo.local.recordReverseTree` | `String` | User property for reverse dependency tree. If enabled, Maven will record ".tracking" directory into local repository with "reverse dependency tree", essentially explaining WHY given artifact is present in local repository. Default: <code>false</code>, will not record anything. | `false` | 3.9.0 | User properties |
| 17. | `maven.repo.local.tail` | `String` | User property for chained LRM: list of "tail" local repository paths (separated by comma), to be used with {@code org.eclipse.aether.util.repository.ChainedLocalRepositoryManager} . Default value: <code>null</code>, no chained LRM is used. | - | 3.9.0 | User properties |
| 18. | `maven.resolver.dependencyManagerTransitivity` | `String` | User property for selecting dependency manager behaviour regarding transitive dependencies and dependency management entries in their POMs. Maven 3 targeted full backward compatibility with Maven2, hence it ignored dependency management entries in transitive dependency POMs. Maven 4 enables "transitivity" by default, hence unlike Maven2, obeys dependency management entries deep in dependency graph as well. <br/> Default: <code>"true"</code>. | `true` | 4.0.0 | User properties |
| 19. | `maven.resolver.transport` | `String` | Resolver transport to use. Can be <code>default</code>, <code>wagon</code>, <code>apache</code>, <code>jdk</code> or <code>auto</code>. | `default` | 4.0.0 | User properties |
| 20. | `maven.style.color` | `String` | Maven output color mode. Allowed values are <code>auto</code>, <code>always</code>, <code>never</code>. | `auto` | 4.0.0 | User properties |
| 21. | `maven.user.conf` | `String` | Maven user configuration directory. | `${user.home}/.m2` | 4.0.0 | User properties |
| 22. | `maven.user.extensions` | `String` | Maven user extensions. | `${maven.user.conf}/extensions.xml` | 4.0.0 | User properties |
| 23. | `maven.user.settings` | `String` | Maven user settings. | `${maven.user.conf}/settings.xml` | 4.0.0 | User properties |
| 24. | `maven.user.toolchains` | `String` | Maven user toolchains. | `${maven.user.home}/toolchains.xml` | 4.0.0 | User properties |
| 25. | `maven.versionFilters` | `String` | User property for version filters expression, a semicolon separated list of filters to apply. By default, no version filter is applied (like in Maven 3). <br/> Supported filters: <ul> <li>"h" or "h(num)" - highest version or top list of highest ones filter</li> <li>"l" or "l(num)" - lowest version or bottom list of lowest ones filter</li> <li>"s" - contextual snapshot filter</li> <li>"e(G:A:V)" - predicate filter (leaves out G:A:V from range, if hit, V can be range)</li> </ul> Example filter expression: <code>"h(5);s;e(org.foo:bar:1)</code> will cause: ranges are filtered for "top 5" (instead full range), snapshots are banned if root project is not a snapshot, and if range for <code>org.foo:bar</code> is being processed, version 1 is omitted. | - | 4.0.0 | User properties |