Skip to content

Conversation

cstamas
Copy link
Member

@cstamas cstamas commented May 22, 2025

Simple ability to make Maven 4 "work like Maven 3" would.

Changes:

  • Resolver session (scope manager, transitive dep mgr) - DONE
  • model validation (in "Maven3" mode model 4.1.0 should be rejected) - DONE
  • scope validation (obey what scopeMgr holds) - DONE
  • inline POM transformation in Maven3 mode (instead of consumer POM transformation) - DONE
  • added IT that confirms: maven4 in maven3 mode does not need flatten plugin

Issues:
https://issues.apache.org/jira/browse/MNG-8742
https://issues.apache.org/jira/browse/MNG-8743
https://issues.apache.org/jira/browse/MNG-8744

Simple ability to make Maven 4 "work like Maven 3" would.
TBD "inliner" transformer.
@cstamas cstamas self-assigned this May 22, 2025
@cstamas cstamas requested a review from gnodet May 22, 2025 21:07
* @since 4.0.0
*/
@Config(type = "java.lang.Boolean", defaultValue = "true")
public static final String MAVEN_MODERN_PERSONALITY = "maven.modernPersonality";
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it be more intuitive to have a config 'maven3compatibility' (I don't like this naming either) with a default value to 'false' ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Agreed, but went with maven.maven3Personality as Maven4 is per def "compatible" with Maven3, this is more like "run Maven4 fully in Maven3 mode".

@cstamas cstamas requested a review from gnodet May 23, 2025 16:18
@cstamas cstamas marked this pull request as ready for review May 23, 2025 17:45
*
* @since 4.0.0
*/
@Config(type = "java.lang.Boolean", defaultValue = "false")
Copy link
Contributor

Choose a reason for hiding this comment

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

MAVEN_MAVEN3_PERSONALITY sounds weird and duplicative. Maybe just MAVEN3_PERSONALITY?

Copy link
Member Author

Choose a reason for hiding this comment

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

This is really just to keep the "convention" of maven. prefixed properties. I agree, is awkward, but that is what it is.

Copy link
Contributor

Choose a reason for hiding this comment

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

The principle I picked up at Google (where this was explicitly called out) is to always follow the style guidelines even when surrounding existing code does not. Consistency does not trump correctness.

Copy link
Contributor

Choose a reason for hiding this comment

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

If we consider prefixing incorrect, then we should change them all.

String MODEL_VERSION_4_1_0 = "4.1.0";

List<String> VALID_MODEL_VERSIONS = List.of(MODEL_VERSION_4_0_0, MODEL_VERSION_4_1_0);
List<String> ALL_KNOWN_MODEL_VERSIONS = List.of(MODEL_VERSION_4_0_0, MODEL_VERSION_4_1_0);
Copy link
Contributor

Choose a reason for hiding this comment

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

KNOWN_MODEL_VERSIONS (delete ALL_)

public void injectTransformedArtifacts(RepositorySystemSession session, MavenProject currentProject)
throws IOException {
if (!Features.consumerPom(session.getConfigProperties())) {
// TODO
Copy link
Contributor

Choose a reason for hiding this comment

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

do this here?

Copy link
Member Author

Choose a reason for hiding this comment

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

marked as draft

@Override
@SuppressWarnings("checkstyle:MethodLength")
public void validateFileModel(Model m, int validationLevel, ModelProblemCollector problems) {
public void validateFileModel(Session s, Model m, int validationLevel, ModelProblemCollector problems) {
Copy link
Contributor

Choose a reason for hiding this comment

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

s --> session
wouldn't hurt to rename m to model too

Copy link
Member Author

@cstamas cstamas May 23, 2025

Choose a reason for hiding this comment

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

Yeah, when am modifying existing code, I am trying to "adhere" to existing "style". I agree, but doing that (in fact doing all what you propose) would come with a LOT of unrelated diff...

@cstamas cstamas marked this pull request as draft May 23, 2025 21:11
Copy link
Contributor

@Pankraz76 Pankraz76 left a comment

Choose a reason for hiding this comment

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

nicely done.

import org.eclipse.aether.impl.scope.ScopeManagerConfiguration;
import org.eclipse.aether.internal.impl.scope.ScopeManagerDump;
import org.eclipse.aether.scope.DependencyScope;
import org.eclipse.aether.scope.ResolutionScope;
Copy link
Contributor

Choose a reason for hiding this comment

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

might redo

Copy link
Member Author

Choose a reason for hiding this comment

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

and what should I do with already imported org.apache.maven.api.DependencyScope?

Copy link
Contributor

Choose a reason for hiding this comment

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

ah sorry. I was expecting this to cause the full qualified access below. suspecting CI to avoid this in first place.

.orElse(null);

ArrayList<ResolutionScope> result = new ArrayList<>();
ArrayList<org.eclipse.aether.scope.ResolutionScope> result = new ArrayList<>();
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
ArrayList<org.eclipse.aether.scope.ResolutionScope> result = new ArrayList<>();
ArrayList<ResolutionScope> result = new ArrayList<>();

might use import again.

| `maven.logger.showThreadId` | `Boolean` | If you would like to output the current thread id, then set to true. Defaults to false. | `false` | 4.0.0 | User properties |
| `maven.logger.showThreadName` | `Boolean` | Set to true if you want to output the current thread name. Defaults to true. | `true` | 4.0.0 | User properties |
| `maven.logger.warnLevelString` | `String` | The string value output for the warn level. Defaults to WARN. | `WARN` | 4.0.0 | User properties |
| `maven.maven3Personality` | `Boolean` | User property for controlling "maven personality". If set to {@code true} Maven will behave as previous major version, Maven 3. | `false` | 4.0.0 | User properties |
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
| `maven.maven3Personality` | `Boolean` | User property for controlling "maven personality". If set to {@code true} Maven will behave as previous major version, Maven 3. | `false` | 4.0.0 | User properties |
| `maven.maven3Personality` | `Boolean` | User property for controlling "maven personality". If activated Maven will behave as previous major version, Maven 3. | `false` | 4.0.0 | User properties |

this is just an idea as true/false is kind of technical detail and quite known.
Might give business case more attention, rather then some data type used.

Copy link
Member Author

Choose a reason for hiding this comment

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

This is generated content.

String newVersion = version;
int lastEnd = -1;
if (version != null) {
Matcher m = PLACEHOLDER_EXPRESSION.matcher(version.trim());
Copy link
Contributor

Choose a reason for hiding this comment

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

Using the Interpolator would be easier and safer here.

Copy link
Contributor

@Pankraz76 Pankraz76 left a comment

Choose a reason for hiding this comment

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

nice coding so far.

throw new IllegalStateException("This transformer does not use this call.");
}

protected static final String NAMESPACE_FORMAT = "http://maven.apache.org/POM/%s";
Copy link
Contributor

Choose a reason for hiding this comment

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

consider grouping all fields like we would do for constructors: https://checkstyle.sourceforge.io/checks/coding/constructorsdeclarationgrouping.html

Comment on lines 56 to 79
Matcher m = PLACEHOLDER_EXPRESSION.matcher(version.trim());
while (m.find()) {
String property = m.group(1);
if (!session.getConfigProperties().containsKey(property)) {
throw new IllegalArgumentException("Cannot inline property " + property);
}
String propertyValue =
(String) session.getConfigProperties().get(property);
if (lastEnd < 0) {
newVersion = version.substring(0, m.start());
} else {
newVersion += version.substring(lastEnd, m.start());
}
lastEnd = m.end();
newVersion += propertyValue;
}
if (!Objects.equals(version, newVersion)) {
model = model.withVersion(newVersion);
Path tmpPom = Files.createTempFile(
project.getArtifactId() + "-" + project.getVersion() + "-", ".xml");
write(model, tmpPom);
project.setFile(tmpPom.toFile());
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Matcher m = PLACEHOLDER_EXPRESSION.matcher(version.trim());
while (m.find()) {
String property = m.group(1);
if (!session.getConfigProperties().containsKey(property)) {
throw new IllegalArgumentException("Cannot inline property " + property);
}
String propertyValue =
(String) session.getConfigProperties().get(property);
if (lastEnd < 0) {
newVersion = version.substring(0, m.start());
} else {
newVersion += version.substring(lastEnd, m.start());
}
lastEnd = m.end();
newVersion += propertyValue;
}
if (!Objects.equals(version, newVersion)) {
model = model.withVersion(newVersion);
Path tmpPom = Files.createTempFile(
project.getArtifactId() + "-" + project.getVersion() + "-", ".xml");
write(model, tmpPom);
project.setFile(tmpPom.toFile());
}
}
handleVersion(session, project, version, -1, version, model);

could give dedicated attention to this concern. Operating on all the fields is kind of smell also.

    private static void handleVersion(RepositorySystemSession session, MavenProject project, String version, int lastEnd, String newVersion, Model model) throws IOException {
        Matcher m = PLACEHOLDER_EXPRESSION.matcher(version.trim());
        while (m.find()) {
            String property = m.group(1);
            if (!session.getConfigProperties().containsKey(property)) {
                throw new IllegalArgumentException("Cannot inline property " + property);
            }
            String propertyValue =
                    (String) session.getConfigProperties().get(property);
            if (lastEnd < 0) {
                newVersion = version.substring(0, m.start());
            } else {
                newVersion += version.substring(lastEnd, m.start());
            }
            lastEnd = m.end();
            newVersion += propertyValue;
        }
        if (!Objects.equals(version, newVersion)) {
            model = model.withVersion(newVersion);
            Path tmpPom = Files.createTempFile(
                    project.getArtifactId() + "-" + project.getVersion() + "-", ".xml");
            write(model, tmpPom);
            project.setFile(tmpPom.toFile());
        }
    }

Comment on lines 71 to 74
MavenStaxReader reader = new MavenStaxReader();
try (InputStream is = Files.newInputStream(src)) {
return reader.read(is, false, null);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
MavenStaxReader reader = new MavenStaxReader();
try (InputStream is = Files.newInputStream(src)) {
return reader.read(is, false, null);
}
try (InputStream is = Files.newInputStream(src)) {
return new MavenStaxReader().read(is, false, null);
}

could inline.

Comment on lines +78 to +87
String version = model.getModelVersion();
Files.createDirectories(dest.getParent());
try (Writer w = Files.newBufferedWriter(dest)) {
MavenStaxWriter writer = new MavenStaxWriter();
writer.setNamespace(String.format(NAMESPACE_FORMAT, version));
writer.setSchemaLocation(String.format(SCHEMA_LOCATION_FORMAT, version));
writer.setAddLocationInformation(false);
writer.write(w, model);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
String version = model.getModelVersion();
Files.createDirectories(dest.getParent());
try (Writer w = Files.newBufferedWriter(dest)) {
MavenStaxWriter writer = new MavenStaxWriter();
writer.setNamespace(String.format(NAMESPACE_FORMAT, version));
writer.setSchemaLocation(String.format(SCHEMA_LOCATION_FORMAT, version));
writer.setAddLocationInformation(false);
writer.write(w, model);
}
}
Files.createDirectories(dest.getParent());
try (Writer w = Files.newBufferedWriter(dest)) {
write(model.getModelVersion(), w, new MavenStaxWriter())
}
}

could delegate as well.


String MODEL_VERSION_4_1_0 = "4.1.0";

List<String> ALL_KNOWN_MODEL_VERSIONS = List.of(MODEL_VERSION_4_0_0, MODEL_VERSION_4_1_0);
Copy link
Contributor

Choose a reason for hiding this comment

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

yes, all is kind of implied.

@cstamas cstamas marked this pull request as ready for review June 4, 2025 14:23
@cstamas cstamas requested a review from slawekjaranowski June 4, 2025 14:23
Copy link
Contributor

@Pankraz76 Pankraz76 left a comment

Choose a reason for hiding this comment

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

good structure already.

project.getArtifactId() + "-" + project.getVersion() + "-", ".xml");
write(model, tmpPom);
project.setFile(tmpPom.toFile());
needsInlining(session).addAll(usedProperties);
Copy link
Contributor

Choose a reason for hiding this comment

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

yes.

Copy link
Contributor

Choose a reason for hiding this comment

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

might give dedication as well, if makes sense.

Suggested change
needsInlining(session).addAll(usedProperties);
needsInlining(session).addAll(getUsedProperties(interpolator));

might be not performant as n+1 iteration.

because the code does something like simple map operation on kind collection interpolator.interpolate(). Idk this structure so well im sorry. Somehow can not reach the code, so my knowledge very short.

}
String newVersion;
if (version != null) {
HashSet<String> usedProperties = new HashSet<>();
Copy link
Contributor

Choose a reason for hiding this comment

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

couplin.

return (String) session.getConfigProperties().get(property);
});
if (!Objects.equals(version, newVersion)) {
if (parentVersion) {
Copy link
Contributor

Choose a reason for hiding this comment

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

cohesion. good giving dedicated concern.

return artifacts;
}
ArrayList<Artifact> newArtifacts = new ArrayList<>(artifacts.size());
for (Artifact artifact : artifacts) {
Copy link
Contributor

@Pankraz76 Pankraz76 Jun 4, 2025

Choose a reason for hiding this comment

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

Suggested change
for (Artifact artifact : artifacts) {
return artifacts.map(....)

could avoid overhead, use only for body as map operation.

Its already very nice functional and compact. Lets complete this intent.

its just 1 LOC

return isEmpty() ? artifacts : artifacts.map(toFoo())

@gnodet gnodet added the enhancement New feature or request label Jun 4, 2025
@gnodet gnodet added this to the 4.0.0-rc-4 milestone Jun 4, 2025
MavenStaxReader reader = new MavenStaxReader();
try (InputStream is = Files.newInputStream(src)) {
return reader.read(is, false, null);
return new MavenStaxReader().read(is, false, null);
Copy link
Contributor

Choose a reason for hiding this comment

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

nice.

if (!Objects.equals(version, newVersion)) {
model = model.withVersion(newVersion);
if (parentVersion) {
model = model.withParent(model.getParent().withVersion(newVersion));
Copy link
Contributor

Choose a reason for hiding this comment

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

model = parentVersion 
               ? model.withParent(model.getParent().withVersion(newVersion))
               : model.withVersion(newVersion);

@cstamas cstamas merged commit 5000b59 into apache:master Jun 6, 2025
18 of 19 checks passed
@cstamas cstamas deleted the maven4-maven3-mode branch June 6, 2025 20:01
@jira-importer
Copy link

Resolve #9552

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

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants