Skip to content

[MNG-5913] Allow defining aliases for existing server configurations in settings.xml#2333

Open
slawekjaranowski wants to merge 11 commits into
apache:masterfrom
slawekjaranowski:MNG-5913
Open

[MNG-5913] Allow defining aliases for existing server configurations in settings.xml#2333
slawekjaranowski wants to merge 11 commits into
apache:masterfrom
slawekjaranowski:MNG-5913

Conversation

@slawekjaranowski

Copy link
Copy Markdown
Member

Add the next tag ids to the server in settings.xml

Additional server will be created in memory by SettingsBuilder, so the generated configuration should be transparent to other as Settings#getServers() will return complete list.


https://issues.apache.org/jira/browse/MNG-5913

@slawekjaranowski slawekjaranowski added the enhancement New feature or request label May 13, 2025
@slawekjaranowski slawekjaranowski self-assigned this May 13, 2025
Comment thread api/maven-api-settings/src/main/mdo/settings.mdo Outdated
Comment thread api/maven-api-settings/src/main/mdo/settings.mdo
@cstamas

cstamas commented May 13, 2025

Copy link
Copy Markdown
Member

This needs to be made sure will not break Maven 3, as same settings is shared by both currently.

@slawekjaranowski

Copy link
Copy Markdown
Member Author

This needs to be made sure will not break Maven 3, as same settings is shared by both currently.

unit test, looks ok for me.

I tested with: mvn help:effective-settings

@slawekjaranowski

Copy link
Copy Markdown
Member Author

ok, who is willing to work on it with me?
If proposition will be generally accepted I will do similar in 3.9.x

I hope such feature will be valuable.

@cstamas

cstamas commented May 14, 2025

Copy link
Copy Markdown
Member

Well, settings with current 3.9.9 are NOT read:

[WARNING] 
[WARNING] Some problems were encountered while building the effective settings
[WARNING] Unrecognised tag: 'aliases' (position: START_TAG seen ...</id>\n     <aliases>... @61:15)  @ /home/cstamas/.m2/settings.xml, line 61, column 15
[WARNING] 

Or this is "ok"? Is settings loaded partially or discaded?

@cstamas

cstamas commented May 14, 2025

Copy link
Copy Markdown
Member

I am willing to jump in as Njord does something similar, but my concern is that Maven 3.x is not "forgiving" while readint settings.xml AFAIK. And if we modify XML then things like ./mvnw will work ONLY (for user) if 3.9.10 used?

@slawekjaranowski

Copy link
Copy Markdown
Member Author

Well, settings with current 3.9.9 are NOT read:

[WARNING] 
[WARNING] Some problems were encountered while building the effective settings
[WARNING] Unrecognised tag: 'aliases' (position: START_TAG seen ...</id>\n     <aliases>... @61:15)  @ /home/cstamas/.m2/settings.xml, line 61, column 15
[WARNING] 

Or this is "ok"? Is settings loaded partially or discaded?

it is correct because 3.9.x does not have it yet

@slawekjaranowski

Copy link
Copy Markdown
Member Author

I am willing to jump in as Njord does something similar, but my concern is that Maven 3.x is not "forgiving" while readint settings.xml AFAIK. And if we modify XML then things like ./mvnw will work ONLY (for user) if 3.9.10 used?

I hope we can have next version of settings schema for 3.9.x ... so if someone update own settings alos will use newer version of Maven

@Pankraz76 Pankraz76 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

already very nice.
please give last dedication, to reach infinitive higher-order function.

Comment thread impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultSettingsBuilder.java Outdated

@Pankraz76 Pankraz76 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

polish

Comment thread impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultSettingsBuilder.java Outdated
Comment thread impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultSettingsBuilder.java Outdated
@slawekjaranowski

Copy link
Copy Markdown
Member Author

@gnodet @Pankraz76 last suggestions applied

@slawekjaranowski

Copy link
Copy Markdown
Member Author

@michael-o @cstamas I refreshed a PR 😄 with last suggestions

to build pass I need fixes in modello - in progress

@michael-o

Copy link
Copy Markdown
Member

Will check next week...

Comment thread api/maven-api-settings/src/main/mdo/settings.mdo Outdated
.id(id)
.aliases(List.of())
.build());
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks very ugly. It is the clien't responsibility to traverse the Settings object, no?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is the core of change.

Based on additional tag - aliases we create a next server instance in memory on server list.
So all others plugins tools will see destination list of servers original declared in settings and created based on aliases tag.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do remember that Modello allows to add Java code to Settings. Why can't we inject/improve the code in the model directly?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so you propose to override a getServers method directly in modelo?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, correct.

@slawekjaranowski slawekjaranowski Apr 14, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@michael-o
It will be impossible (in easy way) to implement in code settings.mdo, we use velocity templates to generate code for 4.x and for 3.x.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Share the spot where this is done. Modello made it possible :-(

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Share the spot where this is done. Modello made it possible :-(

There is two templates for generating output from modello files:
https://github.com/apache/maven/blob/master/src/mdo/model.vm
https://github.com/apache/maven/blob/master/src/mdo/model-v3.vm

I see some of code in settings.mdo so will look again

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see a way to provide such code in settings.mdo

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@michael-o any other remarks as implementation in modelo is not easy

@slawekjaranowski

Copy link
Copy Markdown
Member Author

Yet another problem is that we are abusing one server structure for multiple ones, e.g., you are settings headers or other transport-related config. It will be applied to mulitple servers, but you need to split them up actually except credentials.

aliases will be for simple case - when all items from server should be the same - it is the most common case.
For more complicated situation you still need define a separate servers items

@michael-o

Copy link
Copy Markdown
Member

Looking into now.

@michael-o

Copy link
Copy Markdown
Member

Here is the magic:

public Server getServer(String serverId) {
Server match = null;
java.util.List<Server> servers = getServers();
if (servers != null && serverId != null) {
for (Server server : servers) {
if (serverId.equals(server.getId())) {
match = server;
break;
}
}
}
return match;
}

Why not change it and make as easy as possible?

@slawekjaranowski

Copy link
Copy Markdown
Member Author

Here is the magic:

public Server getServer(String serverId) {
Server match = null;
java.util.List<Server> servers = getServers();
if (servers != null && serverId != null) {
for (Server server : servers) {
if (serverId.equals(server.getId())) {
match = server;
break;
}
}
}
return match;
}

Why not change it and make as easy as possible?

it is implementation of method Server getServer(String serverId) so it is server by id ....

  • looks not used in Maven core v3
  • it is not generated in Maven v4

I need to change a method List<Server> getServers()

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this, @slawekjaranowski -- this is a frequently requested feature (MNG-5913 is from 2016!) and the approach of expanding aliases into synthetic Server entries during settings building is a clean way to make it transparent to the rest of Maven.

I reviewed the full diff carefully. The overall design is sound: alias expansion happens before validation (so duplicate-id detection catches alias clashes with explicit servers), and the !isProjectSettings guard in the impl builder correctly prevents alias expansion for project settings. A few findings below.

Bug: copy-paste error in test assertions (both compat and impl)

Both testSettingsWithServersAndAliases test methods assert server11.getAliases().isEmpty() on the line where server12.getAliases() should be checked. The test still passes because server11 happens to have empty aliases too, but the assertion is not verifying what it claims to.

Missing edge-case validation: alias equals own server id

The current implementation does not validate that an alias is different from the server's own id. If a user writes <id>server-1</id><aliases><alias>server-1</alias></aliases>, the expansion would produce a duplicate server-1 entry. The validator would catch it as a duplicate-id warning, but it would be clearer to reject it earlier with a specific message like "alias must not equal the server's own id".

Missing edge-case validation: same alias used by multiple servers

If two servers both declare <alias>shared</alias>, the expansion produces two servers with id shared. Again, the validator catches this as a duplicate, but the error message ("duplicate server with id shared") does not help the user understand that the problem is in their alias configuration. A dedicated check in the expansion step could produce a more actionable message.

Missing test: alias equals server id of another explicitly declared server (the settings-servers-3 scenario with assertion)

testSettingsWithDuplicateServersIds does test this path and checks for the warning, which is good. But it would also be valuable to test that the correct server configuration wins (the explicitly declared one vs. the alias-expanded one), since the order in the list determines which one downstream code picks up.

Review by Claude Code on behalf of @gnodet

return settings;
}

private List<Server> serversByIds(List<Server> servers) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding validation here (or at least in the validator) that an alias does not equal the server's own id, and that the same alias string is not used by multiple servers. The existing duplicate-id check in DefaultSettingsValidator will catch both cases, but the resulting error message ("duplicate server with id X") does not help the user understand whether the problem is in their <aliases> configuration. A more specific message during alias expansion would be more actionable.

For example:

private List<Server> serversByIds(List<Server> servers) {
    Set<String> allIds = new HashSet<>();
    return servers.stream()
            .flatMap(server -> {
                if (!allIds.add(server.getId())) {
                    // will be caught by validator
                }
                return Stream.concat(
                        Stream.of(server),
                        server.getAliases().stream().map(alias -> {
                            if (alias.equals(server.getId())) {
                                // report: alias must differ from server id
                            }
                            if (!allIds.add(alias)) {
                                // report: alias clashes with another server/alias
                            }
                            return serverAlias(server, alias);
                        }));
            })
            .toList();
}

This is a suggestion for a potential follow-up rather than a blocker.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will look at it

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gnodet I have added a validation for server.aliases

Please look again

<field>
<name>aliases</name>
<version>1.3.0+</version>
<description>List of additional server aliases. For each alias, an additional server will be generated by copying the entire configuration, but using a different ID.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: The description mentions "an additional server will be generated by copying the entire configuration" -- it may be worth noting that the <aliases> list itself is NOT copied to the generated server entries (they get an empty aliases list). This is important for users to understand: aliases are not transitive.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

description reformat to include info about empty aliases in generated servers

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

This is a frequently requested feature (MNG-5913, open since 2015, 3 votes, 6 watchers). The approach of expanding aliases into synthetic Server entries during settings building is clean and transparent to the rest of Maven.

Design: Sound. Alias expansion happens after validation (so conflict detection works correctly), and the !isProjectSettings guard in the impl builder correctly prevents expansion for project settings. Downstream code (e.g. DefaultRepositorySystemSessionFactory) iterates getServers() unchanged — no modifications needed outside the settings builder/validator.

Backward compatibility: Acceptable. Older Maven versions warn about the unrecognized <aliases> tag but continue processing all other settings correctly.

Previous review findings (copy-paste test bug, missing alias-vs-self/alias-vs-alias validation): addressed in commits d1916cc7 and 803b8170. A few new findings below — mostly minor wording and validation gaps.

Fully automatic review from Claude Code

Comment on lines +535 to +538
<description>List of additional server aliases. For each alias, an additional server will be generated.
Genearte servers items will have the same configuration as the original one, but with the ID specified in this list
and with an empty aliases list.
This is useful when the same credentials should be used for multiple servers.</description>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: "Genearte" → "Generated". Also minor wording improvement for clarity.

Suggested change
<description>List of additional server aliases. For each alias, an additional server will be generated.
Genearte servers items will have the same configuration as the original one, but with the ID specified in this list
and with an empty aliases list.
This is useful when the same credentials should be used for multiple servers.</description>
<description>List of additional server aliases. For each alias, an additional server entry will be generated
with the same configuration as the original one, but with the ID replaced by the alias
and with an empty aliases list.
This is useful when the same credentials should be used for multiple servers.</description>


for (int i = 0; i < servers.size(); i++) {
Server server = servers.get(i);
for (String alias : server.getAliases()) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding validation for empty or blank alias strings before the uniqueness check. An <alias></alias> or <alias> </alias> entry would silently produce a server with an empty/blank ID, which would cause hard-to-diagnose issues downstream.

Something like:

validateStringNotEmpty(problems, "servers.server[" + i + "].aliases", alias, server.getId());

before the serverIds.add check.

Same applies to the compat validator.

BuilderProblem.Severity.WARNING,
"servers.server[" + i + "].aliases",
server.getId(),
"must be unique for all servers id but found duplicate alias " + alias);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor grammar: "for all servers id" reads awkwardly.

Suggested change
"must be unique for all servers id but found duplicate alias " + alias);
"must be unique across all server ids and aliases but found duplicate alias " + alias);

}

settingsValidator.validate(settings, problems);
settings.setServers(serversByIds(settings.getServers()));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

serversByIds() uses Stream.toList() which returns an unmodifiable list. The Modello-generated setServers() stores the reference directly, so any downstream code calling getServers().add(...) would throw UnsupportedOperationException.

This is likely fine since settings aren't mutated after building, but if safety is preferred, consider wrapping: new ArrayList<>(serversByIds(...)) (would need the import).

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up review — Slawek addressed the main feedback from the previous round (renamed ids to aliases, simplified serversByIds with Stream.concat + helper, .toList(), List.of(), added validation and comprehensive tests). Nice work!

One issue needs fixing before merge (see inline), plus a minor style nit.

Fully automatic review from Claude Code — this review does not replace specialized review tools such as CodeRabbit, Sourcery, or SonarCloud.

</models>
<params>
<param>forcedIOModelVersion=1.2.0</param>
<param>forcedIOModelVersion=1.3.0</param>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The compat/maven-settings module is missing <pluralExceptions> in its modello configuration — both api/maven-api-settings/pom.xml and impl/maven-support/pom.xml have it:

<pluralExceptions>
  <aliases>alias</aliases>
</pluralExceptions>

Without it, modello auto-depluralize "aliases" by stripping the trailing s, producing addAliase() / removeAliase() instead of the correct addAlias() / removeAlias(). The tests already confirm this — they call server.addAliase(...). Once this ships in a release, that method name becomes permanent public API.

The XML parsing is unaffected (the reader/writer in maven-support has the correct config and uses <alias>), but the compat Java API will have an ungrammatical method name forever.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch - thanks

Comment on lines 210 to 212
}

if (isProjectSettings) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: these two blocks are mutually exclusive — combining into if/else would make that obvious.

Suggested change
}
if (isProjectSettings) {
} else {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request mvn4

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants