Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions impl/maven-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ under the License.
<name>Maven 4 Core</name>
<description>Maven Core classes.</description>

<properties>
<!-- in: PomConstructionTest -->
<checkstyle.violation.ignore>FileLength</checkstyle.violation.ignore>
</properties>

<dependencies>
<!-- Maven4 API -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1534,6 +1534,82 @@ void testProfilePluginMngDependencies() throws Exception {
assertEquals("a", pom.getValue("build/plugins[1]/dependencies[1]/artifactId"));
}

/* MNG-3309 */
@Test
void testCascadingProfileActivation() throws Exception {
Properties props = new Properties();
props.put("trigger", "start");

PomTestWrapper pom = buildPom("cascading-profile-activation", props, null);

// Verify that cascading profile activation works
// profile1 should be activated by trigger=start
// profile2 should be activated by profile1's cascade.level1=activate property
// profile3 should be activated by profile2's cascade.level2=activate property

List<org.apache.maven.model.Profile> activeProfiles =
pom.getMavenProject().getActiveProfiles();

// Should have 3 active profiles (profile1, profile2, profile3)
assertEquals(3, activeProfiles.size());

// Verify specific profiles are active
assertTrue(activeProfiles.stream().anyMatch(p -> "profile1".equals(p.getId())));
assertTrue(activeProfiles.stream().anyMatch(p -> "profile2".equals(p.getId())));
assertTrue(activeProfiles.stream().anyMatch(p -> "profile3".equals(p.getId())));

// Verify profile4 is NOT active (no trigger)
assertTrue(activeProfiles.stream().noneMatch(p -> "profile4".equals(p.getId())));

// Verify properties are set correctly (last profile wins)
assertEquals("profile3", pom.getValue("properties/test.property"));
assertEquals("true", pom.getValue("properties/profile1.activated"));
assertEquals("true", pom.getValue("properties/profile2.activated"));
assertEquals("true", pom.getValue("properties/profile3.activated"));
}

/* MNG-3309 - Test circular dependency handling */
@Test
void testCascadingProfileActivationCircular() throws Exception {
Properties props = new Properties();
props.put("circular", "test");

PomTestWrapper pom = buildPom("cascading-profile-activation", props, null);

// Verify that circular dependencies are handled gracefully
// profile5 sets trigger=start which would activate profile1
// But this should not cause infinite loops

List<org.apache.maven.model.Profile> activeProfiles =
pom.getMavenProject().getActiveProfiles();

// Should have profile5 and profile1 active (and cascaded profiles)
assertTrue(activeProfiles.stream().anyMatch(p -> "profile5".equals(p.getId())));
assertTrue(activeProfiles.stream().anyMatch(p -> "profile1".equals(p.getId())));

// Verify no infinite loop occurred (test should complete)
assertTrue(activeProfiles.size() >= 2);
}

/* MNG-3309 - Test no cascading baseline */
@Test
void testNoCascadingProfileActivation() throws Exception {
// Test with no trigger properties - no profiles should be activated
PomTestWrapper pom = buildPom("cascading-profile-activation");

List<org.apache.maven.model.Profile> activeProfiles =
pom.getMavenProject().getActiveProfiles();

// No profiles should be active
assertEquals(0, activeProfiles.size());

// Properties should remain at default values
assertEquals("default", pom.getValue("properties/test.property"));
assertEquals("false", pom.getValue("properties/profile1.activated"));
assertEquals("false", pom.getValue("properties/profile2.activated"));
assertEquals("false", pom.getValue("properties/profile3.activated"));
}

/** MNG-4116 */
@Test
void testPercentEncodedUrlsMustNotBeDecoded() throws Exception {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>org.apache.maven.its.mng3309</groupId>
<artifactId>cascading-profile-activation</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>Maven Integration Test :: MNG-3309</name>
<description>
Test cascading profile activation where one profile's properties trigger the activation of other profiles.
</description>

<properties>
<!-- Default properties -->
<test.property>default</test.property>
<profile1.activated>false</profile1.activated>
<profile2.activated>false</profile2.activated>
<profile3.activated>false</profile3.activated>
</properties>

<profiles>
<!-- Profile 1: Activated by system property trigger=start -->
<profile>
<id>profile1</id>
<activation>
<property>
<name>trigger</name>
<value>start</value>
</property>
</activation>
<properties>
<profile1.activated>true</profile1.activated>
<!-- This property should trigger profile2 -->
<cascade.level1>activate</cascade.level1>
<test.property>profile1</test.property>
</properties>
</profile>

<!-- Profile 2: Activated by profile1's cascade.level1 property -->
<profile>
<id>profile2</id>
<activation>
<property>
<name>cascade.level1</name>
<value>activate</value>
</property>
</activation>
<properties>
<profile2.activated>true</profile2.activated>
<!-- This property should trigger profile3 -->
<cascade.level2>activate</cascade.level2>
<test.property>profile2</test.property>
</properties>
</profile>

<!-- Profile 3: Activated by profile2's cascade.level2 property -->
<profile>
<id>profile3</id>
<activation>
<property>
<name>cascade.level2</name>
<value>activate</value>
</property>
</activation>
<properties>
<profile3.activated>true</profile3.activated>
<test.property>profile3</test.property>
</properties>
</profile>

<!-- Profile 4: Should NOT be activated (no trigger) -->
<profile>
<id>profile4</id>
<activation>
<property>
<name>never.set</name>
<value>never</value>
</property>
</activation>
<properties>
<profile4.activated>true</profile4.activated>
<test.property>profile4</test.property>
</properties>
</profile>

<!-- Profile 5: Circular dependency test - sets trigger=start -->
<profile>
<id>profile5</id>
<activation>
<property>
<name>circular</name>
<value>test</value>
</property>
</activation>
<properties>
<profile5.activated>true</profile5.activated>
<!-- This creates a circular dependency with profile1 -->
<trigger>start</trigger>
<test.property>profile5</test.property>
</properties>
</profile>
</profiles>

<build>
<plugins>
<!-- Plugin to help verify which profiles are active -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-help-plugin</artifactId>
<version>3.4.0</version>
<executions>
<execution>
<id>show-profiles</id>
<phase>validate</phase>
<goals>
<goal>active-profiles</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@
*/
package org.apache.maven.api.services.model;

import java.util.Collection;

import org.apache.maven.api.annotations.Nonnull;
import org.apache.maven.api.annotations.Nullable;
import org.apache.maven.api.model.Model;
import org.apache.maven.api.model.Profile;
import org.apache.maven.api.services.InterpolatorException;
import org.apache.maven.api.services.ModelBuilderException;

Expand Down Expand Up @@ -130,4 +133,12 @@ public interface ProfileActivationContext {
* @throws InterpolatorException if an error occurs during interpolation
*/
boolean exists(@Nullable String path, boolean glob);

/**
* Inject properties from newly activated profiles in order to trigger the cascading mechanism.
* This method allows profiles to contribute properties that can trigger the activation of other profiles.
*
* @param activatedProfiles The collection of profiles that have been activated that may trigger the cascading effect.
*/
void addProfileProperties(Collection<Profile> activatedProfiles);
}
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ public static org.apache.maven.api.model.Profile convertFromSettingsProfile(Prof
}

org.apache.maven.api.model.Profile value = profile.build();
value.setSource("settings.xml");
value.setSource(org.apache.maven.api.model.Profile.SOURCE_SETTINGS);
return value;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
Expand All @@ -35,6 +36,7 @@
import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.maven.api.model.Model;
import org.apache.maven.api.model.Profile;
import org.apache.maven.api.services.Interpolator;
import org.apache.maven.api.services.InterpolatorException;
import org.apache.maven.api.services.ModelBuilderException;
Expand Down Expand Up @@ -169,6 +171,7 @@ private boolean matchesExists(Map<ExistRequest, Boolean> exists, DefaultProfileA
private Map<String, String> systemProperties = Collections.emptyMap();
private Map<String, String> userProperties = Collections.emptyMap();
private Model model;
private Map<String, String> cascadingProperties = Collections.emptyMap();
final Record record;

public DefaultProfileActivationContext(
Expand Down Expand Up @@ -326,10 +329,21 @@ public String getModelPackaging() {
@Override
public String getModelProperty(String key) {
if (record != null) {
return record.usedModelProperties.computeIfAbsent(
key, k -> model.getProperties().get(k));
return record.usedModelProperties.computeIfAbsent(key, k -> {
// Check cascading properties first, then model properties
String value = cascadingProperties.get(k);
if (value == null && model.getProperties() != null) {
value = model.getProperties().get(k);
}
return value;
});
} else {
return model.getProperties().get(key);
// Check cascading properties first, then model properties
String value = cascadingProperties.get(key);
if (value == null && model.getProperties() != null) {
value = model.getProperties().get(key);
}
return value;
}
}

Expand Down Expand Up @@ -461,4 +475,25 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
private static Map<String, String> unmodifiable(Map<String, String> map) {
return map != null ? Collections.unmodifiableMap(map) : Collections.emptyMap();
}

// Cascading profile activation methods

@Override
public void addProfileProperties(Collection<Profile> activatedProfiles) {
// Inject properties from activated profiles into cascading properties
// This enables cascading profile activation without modifying the underlying model
if (activatedProfiles != null && !activatedProfiles.isEmpty()) {
Map<String, String> newCascadingProperties = new HashMap<>(cascadingProperties);

// Add properties from each activated profile
for (Profile profile : activatedProfiles) {
if (profile.getProperties() != null) {
newCascadingProperties.putAll(profile.getProperties());
}
}

// Update cascading properties for future profile activation checks
this.cascadingProperties = Collections.unmodifiableMap(newCascadingProperties);
}
}
}
Loading
Loading