From dd4b232520810335b45f1420525dce2c9647593a Mon Sep 17 00:00:00 2001 From: Krasimir Kargov Date: Fri, 19 Dec 2025 14:13:07 +0200 Subject: [PATCH 1/5] Enabling the use of secrets during a deployment LMCROSSITXSADEPLOY-2301 --- .../ExtensionDescriptorChainBuilder.java | 49 ++++++++-- .../ExtensionDescriptorChainBuilderTest.java | 89 +++++++++++++++++++ 2 files changed, 130 insertions(+), 8 deletions(-) diff --git a/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/builders/ExtensionDescriptorChainBuilder.java b/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/builders/ExtensionDescriptorChainBuilder.java index cf65634a..addb942c 100644 --- a/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/builders/ExtensionDescriptorChainBuilder.java +++ b/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/builders/ExtensionDescriptorChainBuilder.java @@ -1,8 +1,10 @@ package org.cloudfoundry.multiapps.mta.builders; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; import org.apache.commons.collections4.CollectionUtils; @@ -16,6 +18,8 @@ public class ExtensionDescriptorChainBuilder { private final boolean isStrict; + private ExtensionDescriptor secureExtensionDescriptor; + public ExtensionDescriptorChainBuilder() { this(true); } @@ -24,8 +28,11 @@ public ExtensionDescriptorChainBuilder(boolean isStrict) { this.isStrict = isStrict; } - public List build(DeploymentDescriptor deploymentDescriptor, List extensionDescriptors) - throws ContentException { + private boolean isSecureDescriptor(ExtensionDescriptor extensionDescriptor) { + return extensionDescriptor.getId().equals("__mta.secure"); + } + + public List build(DeploymentDescriptor deploymentDescriptor, List extensionDescriptors) throws ContentException { Map extensionDescriptorsPerParent = getExtensionDescriptorsPerParent(extensionDescriptors); return build(deploymentDescriptor, extensionDescriptorsPerParent); } @@ -34,11 +41,21 @@ private List build(DeploymentDescriptor deploymentDescripto Map extensionDescriptorsPerParent) { List chain = new ArrayList<>(); Descriptor currentDescriptor = deploymentDescriptor; + ExtensionDescriptor secureDescriptor = null; while (currentDescriptor != null) { ExtensionDescriptor nextDescriptor = extensionDescriptorsPerParent.remove(currentDescriptor.getId()); + if(nextDescriptor != null && isSecureDescriptor(nextDescriptor)) { + secureDescriptor = nextDescriptor; + continue; + } + CollectionUtils.addIgnoreNull(chain, nextDescriptor); currentDescriptor = nextDescriptor; } + CollectionUtils.addIgnoreNull(chain, secureDescriptor); + if(secureDescriptor == null && this.secureExtensionDescriptor != null) { + CollectionUtils.addIgnoreNull(chain, this.secureExtensionDescriptor); + } if (!extensionDescriptorsPerParent.isEmpty() && isStrict) { throw new ContentException(Messages.CANNOT_BUILD_EXTENSION_DESCRIPTOR_CHAIN_BECAUSE_DESCRIPTORS_0_HAVE_AN_UNKNOWN_PARENT, String.join(",", Descriptor.getIds(extensionDescriptorsPerParent.values()))); @@ -47,24 +64,40 @@ private List build(DeploymentDescriptor deploymentDescripto } private Map getExtensionDescriptorsPerParent(List extensionDescriptors) { - Map> extensionDescriptorsPerParent = extensionDescriptors.stream() - .collect(Collectors.groupingBy(ExtensionDescriptor::getParentId)); + Map> extensionDescriptorsPerParent = extensionDescriptors.stream().collect(Collectors.groupingBy(ExtensionDescriptor::getParentId)); + validateSingleExtensionDescriptorPerParent(extensionDescriptorsPerParent); return prune(extensionDescriptorsPerParent); } private Map prune(Map> extensionDescriptorsPerParent) { - validateSingleExtensionDescriptorPerParent(extensionDescriptorsPerParent); + this.secureExtensionDescriptor = null; return extensionDescriptorsPerParent.entrySet() .stream() - .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue() - .get(0))); + .collect(Collectors.toMap(Map.Entry::getKey, entry -> { + List localList = entry.getValue(); + + for(ExtensionDescriptor extensionDescriptor : localList) { + if(extensionDescriptor.getId().equals("__mta.secure")) { + this.secureExtensionDescriptor = extensionDescriptor; + } + } + + for(ExtensionDescriptor extensionDescriptor : localList) { + if(!extensionDescriptor.getId().equals("__mta.secure")) { + return extensionDescriptor; + } + } + + return localList.get(0); + })); } private void validateSingleExtensionDescriptorPerParent(Map> extensionDescriptorsPerParent) { for (Map.Entry> extensionDescriptorsForParent : extensionDescriptorsPerParent.entrySet()) { String parent = extensionDescriptorsForParent.getKey(); List extensionDescriptors = extensionDescriptorsForParent.getValue(); - if (extensionDescriptors.size() > 1 && isStrict) { + long nonSecureCountOfExtensionDescriptors = extensionDescriptors.stream().filter(descriptor -> !descriptor.getId().equals("__mta.secure")).count(); + if (nonSecureCountOfExtensionDescriptors > 1 && isStrict) { throw new ContentException(Messages.MULTIPLE_EXTENSION_DESCRIPTORS_EXTEND_THE_PARENT_0, parent); } } diff --git a/multiapps-mta/src/test/java/org/cloudfoundry/multiapps/mta/builders/ExtensionDescriptorChainBuilderTest.java b/multiapps-mta/src/test/java/org/cloudfoundry/multiapps/mta/builders/ExtensionDescriptorChainBuilderTest.java index 1911e7e1..b4cd9fff 100644 --- a/multiapps-mta/src/test/java/org/cloudfoundry/multiapps/mta/builders/ExtensionDescriptorChainBuilderTest.java +++ b/multiapps-mta/src/test/java/org/cloudfoundry/multiapps/mta/builders/ExtensionDescriptorChainBuilderTest.java @@ -1,7 +1,10 @@ package org.cloudfoundry.multiapps.mta.builders; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.text.MessageFormat; import java.util.Arrays; @@ -102,6 +105,86 @@ void testLaxBuildWithMultipleDescriptorsExtendingTheSameParent() { assertEquals(Collections.emptyList(), extensionDescriptorChain); } + @Test + void testBuildWithNormalAndSecureExtensionDescriptorsWhereSecureIsLastInTheChain() { + DeploymentDescriptor deploymentDescriptor = buildDeploymentDescriptor(FOO_ID); + ExtensionDescriptor normalExtensionDescriptor = buildExtensionDescriptor(BAR_ID, FOO_ID); + ExtensionDescriptor secureExtensionDescriptor = buildSecureExtensionDescriptor(FOO_ID); + + List extensionDescriptors = Arrays.asList(secureExtensionDescriptor, normalExtensionDescriptor); + + ExtensionDescriptorChainBuilder extensionDescriptorChainBuilder = new ExtensionDescriptorChainBuilder(); + List extensionDescriptorChain = extensionDescriptorChainBuilder.build(deploymentDescriptor, extensionDescriptors); + + assertEquals(Arrays.asList(normalExtensionDescriptor, secureExtensionDescriptor), extensionDescriptorChain); + } + + @Test + void testBuildWithOnlySecureExtensionDescriptor() { + DeploymentDescriptor deploymentDescriptor = buildDeploymentDescriptor(FOO_ID); + ExtensionDescriptor secureExtensionDescriptor = buildSecureExtensionDescriptor(FOO_ID); + + List extensionDescriptors = Collections.singletonList(secureExtensionDescriptor); + + ExtensionDescriptorChainBuilder extensionDescriptorChainBuilder = new ExtensionDescriptorChainBuilder(); + List extensionDescriptorChain = extensionDescriptorChainBuilder.build(deploymentDescriptor, extensionDescriptors); + + assertEquals(Collections.singletonList(secureExtensionDescriptor), extensionDescriptorChain); + } + + @Test + void testBuildWhenSecureExtensionDescriptorIsExtendedStrictMode() { + DeploymentDescriptor deploymentDescriptor = buildDeploymentDescriptor(FOO_ID); + ExtensionDescriptor firstExtensionDescriptor = buildExtensionDescriptor(BAR_ID, FOO_ID); + ExtensionDescriptor secureExtensionDescriptor = buildSecureExtensionDescriptor(BAR_ID); + ExtensionDescriptor extensionDescriptorAfterSecureOne = buildExtensionDescriptor(QUX_ID, "__mta.secure"); + + List extensionDescriptors = Arrays.asList(extensionDescriptorAfterSecureOne, firstExtensionDescriptor, secureExtensionDescriptor); + + ExtensionDescriptorChainBuilder extensionDescriptorChainBuilder = new ExtensionDescriptorChainBuilder(true); + + ContentException contentException = assertThrows(ContentException.class, () -> extensionDescriptorChainBuilder.build(deploymentDescriptor, extensionDescriptors)); + + String expectedMessage = MessageFormat.format( + Messages.CANNOT_BUILD_EXTENSION_DESCRIPTOR_CHAIN_BECAUSE_DESCRIPTORS_0_HAVE_AN_UNKNOWN_PARENT, + extensionDescriptorAfterSecureOne.getId() + ); + assertEquals(expectedMessage, contentException.getMessage()); + } + + @Test + void testBuildWhenSecureExtensionDescriptorIsExtendedNonStrictMode() { + DeploymentDescriptor deploymentDescriptor = buildDeploymentDescriptor(FOO_ID); + ExtensionDescriptor firstExtensionDescriptor = buildExtensionDescriptor(BAR_ID, FOO_ID); + ExtensionDescriptor secureExtensionDescriptor = buildSecureExtensionDescriptor(BAR_ID); + ExtensionDescriptor extensionDescriptorAfterSecureOne = buildExtensionDescriptor(QUX_ID, "__mta.secure"); + + List extensionDescriptors = Arrays.asList(extensionDescriptorAfterSecureOne, secureExtensionDescriptor, firstExtensionDescriptor); + + ExtensionDescriptorChainBuilder extensionDescriptorChainBuilder = new ExtensionDescriptorChainBuilder(false); + List extensionDescriptorChain = extensionDescriptorChainBuilder.build(deploymentDescriptor, extensionDescriptors); + + assertEquals(Arrays.asList(firstExtensionDescriptor, secureExtensionDescriptor), extensionDescriptorChain); + } + + @Test + void testBuildWhenTwoSecureExtensionDescriptorsWhereOnlyTheLastOneGetsAppended() { + DeploymentDescriptor deploymentDescriptor = buildDeploymentDescriptor(FOO_ID); + ExtensionDescriptor firstExtensionDescriptor = buildExtensionDescriptor(BAR_ID, FOO_ID); + ExtensionDescriptor firstSecureExtensionDescriptor = buildSecureExtensionDescriptor(FOO_ID); + ExtensionDescriptor secondSecureExtensionDescriptor = buildSecureExtensionDescriptor(FOO_ID); + + List extensionDescriptors = Arrays.asList(firstSecureExtensionDescriptor, firstExtensionDescriptor, secondSecureExtensionDescriptor); + + ExtensionDescriptorChainBuilder extensionDescriptorChainBuilder = new ExtensionDescriptorChainBuilder(); + List extensionDescriptorChain = extensionDescriptorChainBuilder.build(deploymentDescriptor, extensionDescriptors); + + assertEquals(2, extensionDescriptorChain.size()); + assertEquals(firstExtensionDescriptor, extensionDescriptorChain.get(0)); + assertEquals("__mta.secure", extensionDescriptorChain.get(1).getId()); + assertSame(secondSecureExtensionDescriptor, extensionDescriptorChain.get(1)); + } + private DeploymentDescriptor buildDeploymentDescriptor(String id) { return DeploymentDescriptor.createV2() .setId(id); @@ -113,4 +196,10 @@ private ExtensionDescriptor buildExtensionDescriptor(String id, String parentId) .setId(id); } + private ExtensionDescriptor buildSecureExtensionDescriptor(String parentId) { + return ExtensionDescriptor.createV2() + .setParentId(parentId) + .setId("__mta.secure"); + } + } From 5340053be59606ba62c635f15f8673e9fcb661b4 Mon Sep 17 00:00:00 2001 From: Krasimir Kargov Date: Mon, 2 Feb 2026 21:00:52 +0200 Subject: [PATCH 2/5] Fixing comments from review LMCROSSITXSADEPLOY-2301 --- .../cloudfoundry/multiapps/mta/Constants.java | 1 + .../ExtensionDescriptorChainBuilder.java | 16 +++++++++----- .../multiapps/mta/model/ExtensionHook.java | 2 +- .../ExtensionDescriptorChainBuilderTest.java | 21 ++++++++++--------- 4 files changed, 24 insertions(+), 16 deletions(-) diff --git a/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/Constants.java b/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/Constants.java index 70d0fbc0..fdc71d68 100644 --- a/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/Constants.java +++ b/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/Constants.java @@ -7,4 +7,5 @@ private Constants() { public static final String PARAMETER_ELEMENT_TYPE_NAME = "parameter"; public static final String PROPERTY_ELEMENT_TYPE_NAME = "property"; + public static final String SECURE_EXTENSION_DESCRIPTOR_ID = "__mta.secure"; } diff --git a/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/builders/ExtensionDescriptorChainBuilder.java b/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/builders/ExtensionDescriptorChainBuilder.java index addb942c..6e952bc3 100644 --- a/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/builders/ExtensionDescriptorChainBuilder.java +++ b/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/builders/ExtensionDescriptorChainBuilder.java @@ -9,6 +9,7 @@ import org.apache.commons.collections4.CollectionUtils; import org.cloudfoundry.multiapps.common.ContentException; +import org.cloudfoundry.multiapps.mta.Constants; import org.cloudfoundry.multiapps.mta.Messages; import org.cloudfoundry.multiapps.mta.model.DeploymentDescriptor; import org.cloudfoundry.multiapps.mta.model.Descriptor; @@ -29,7 +30,7 @@ public ExtensionDescriptorChainBuilder(boolean isStrict) { } private boolean isSecureDescriptor(ExtensionDescriptor extensionDescriptor) { - return extensionDescriptor.getId().equals("__mta.secure"); + return extensionDescriptor.getId().equals(Constants.SECURE_EXTENSION_DESCRIPTOR_ID); } public List build(DeploymentDescriptor deploymentDescriptor, List extensionDescriptors) throws ContentException { @@ -42,8 +43,10 @@ private List build(DeploymentDescriptor deploymentDescripto List chain = new ArrayList<>(); Descriptor currentDescriptor = deploymentDescriptor; ExtensionDescriptor secureDescriptor = null; + while (currentDescriptor != null) { ExtensionDescriptor nextDescriptor = extensionDescriptorsPerParent.remove(currentDescriptor.getId()); + if(nextDescriptor != null && isSecureDescriptor(nextDescriptor)) { secureDescriptor = nextDescriptor; continue; @@ -52,14 +55,18 @@ private List build(DeploymentDescriptor deploymentDescripto CollectionUtils.addIgnoreNull(chain, nextDescriptor); currentDescriptor = nextDescriptor; } + CollectionUtils.addIgnoreNull(chain, secureDescriptor); + if(secureDescriptor == null && this.secureExtensionDescriptor != null) { CollectionUtils.addIgnoreNull(chain, this.secureExtensionDescriptor); } + if (!extensionDescriptorsPerParent.isEmpty() && isStrict) { throw new ContentException(Messages.CANNOT_BUILD_EXTENSION_DESCRIPTOR_CHAIN_BECAUSE_DESCRIPTORS_0_HAVE_AN_UNKNOWN_PARENT, String.join(",", Descriptor.getIds(extensionDescriptorsPerParent.values()))); } + return chain; } @@ -70,20 +77,19 @@ private Map getExtensionDescriptorsPerParent(List prune(Map> extensionDescriptorsPerParent) { - this.secureExtensionDescriptor = null; return extensionDescriptorsPerParent.entrySet() .stream() .collect(Collectors.toMap(Map.Entry::getKey, entry -> { List localList = entry.getValue(); for(ExtensionDescriptor extensionDescriptor : localList) { - if(extensionDescriptor.getId().equals("__mta.secure")) { + if(extensionDescriptor.getId().equals(Constants.SECURE_EXTENSION_DESCRIPTOR_ID)) { this.secureExtensionDescriptor = extensionDescriptor; } } for(ExtensionDescriptor extensionDescriptor : localList) { - if(!extensionDescriptor.getId().equals("__mta.secure")) { + if(!extensionDescriptor.getId().equals(Constants.SECURE_EXTENSION_DESCRIPTOR_ID)) { return extensionDescriptor; } } @@ -96,7 +102,7 @@ private void validateSingleExtensionDescriptorPerParent(Map> extensionDescriptorsForParent : extensionDescriptorsPerParent.entrySet()) { String parent = extensionDescriptorsForParent.getKey(); List extensionDescriptors = extensionDescriptorsForParent.getValue(); - long nonSecureCountOfExtensionDescriptors = extensionDescriptors.stream().filter(descriptor -> !descriptor.getId().equals("__mta.secure")).count(); + long nonSecureCountOfExtensionDescriptors = extensionDescriptors.stream().filter(descriptor -> !descriptor.getId().equals(Constants.SECURE_EXTENSION_DESCRIPTOR_ID)).count(); if (nonSecureCountOfExtensionDescriptors > 1 && isStrict) { throw new ContentException(Messages.MULTIPLE_EXTENSION_DESCRIPTORS_EXTEND_THE_PARENT_0, parent); } diff --git a/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/model/ExtensionHook.java b/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/model/ExtensionHook.java index eba87204..401d32cc 100644 --- a/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/model/ExtensionHook.java +++ b/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/model/ExtensionHook.java @@ -8,7 +8,7 @@ import org.cloudfoundry.multiapps.common.util.yaml.YamlElement; import org.cloudfoundry.multiapps.mta.parsers.v3.ExtensionHookParser; -public class ExtensionHook extends VersionedEntity implements VisitableElement, NamedElement { +public class ExtensionHook extends VersionedEntity implements VisitableElement, NamedElement, ParametersContainer { // Required by Jackson. protected ExtensionHook() { diff --git a/multiapps-mta/src/test/java/org/cloudfoundry/multiapps/mta/builders/ExtensionDescriptorChainBuilderTest.java b/multiapps-mta/src/test/java/org/cloudfoundry/multiapps/mta/builders/ExtensionDescriptorChainBuilderTest.java index b4cd9fff..b6b49e86 100644 --- a/multiapps-mta/src/test/java/org/cloudfoundry/multiapps/mta/builders/ExtensionDescriptorChainBuilderTest.java +++ b/multiapps-mta/src/test/java/org/cloudfoundry/multiapps/mta/builders/ExtensionDescriptorChainBuilderTest.java @@ -12,6 +12,7 @@ import java.util.List; import org.cloudfoundry.multiapps.common.ContentException; +import org.cloudfoundry.multiapps.mta.Constants; import org.cloudfoundry.multiapps.mta.Messages; import org.cloudfoundry.multiapps.mta.model.DeploymentDescriptor; import org.cloudfoundry.multiapps.mta.model.ExtensionDescriptor; @@ -111,12 +112,12 @@ void testBuildWithNormalAndSecureExtensionDescriptorsWhereSecureIsLastInTheChain ExtensionDescriptor normalExtensionDescriptor = buildExtensionDescriptor(BAR_ID, FOO_ID); ExtensionDescriptor secureExtensionDescriptor = buildSecureExtensionDescriptor(FOO_ID); - List extensionDescriptors = Arrays.asList(secureExtensionDescriptor, normalExtensionDescriptor); + List extensionDescriptors = List.of(secureExtensionDescriptor, normalExtensionDescriptor); ExtensionDescriptorChainBuilder extensionDescriptorChainBuilder = new ExtensionDescriptorChainBuilder(); List extensionDescriptorChain = extensionDescriptorChainBuilder.build(deploymentDescriptor, extensionDescriptors); - assertEquals(Arrays.asList(normalExtensionDescriptor, secureExtensionDescriptor), extensionDescriptorChain); + assertEquals(List.of(normalExtensionDescriptor, secureExtensionDescriptor), extensionDescriptorChain); } @Test @@ -137,9 +138,9 @@ void testBuildWhenSecureExtensionDescriptorIsExtendedStrictMode() { DeploymentDescriptor deploymentDescriptor = buildDeploymentDescriptor(FOO_ID); ExtensionDescriptor firstExtensionDescriptor = buildExtensionDescriptor(BAR_ID, FOO_ID); ExtensionDescriptor secureExtensionDescriptor = buildSecureExtensionDescriptor(BAR_ID); - ExtensionDescriptor extensionDescriptorAfterSecureOne = buildExtensionDescriptor(QUX_ID, "__mta.secure"); + ExtensionDescriptor extensionDescriptorAfterSecureOne = buildExtensionDescriptor(QUX_ID, Constants.SECURE_EXTENSION_DESCRIPTOR_ID); - List extensionDescriptors = Arrays.asList(extensionDescriptorAfterSecureOne, firstExtensionDescriptor, secureExtensionDescriptor); + List extensionDescriptors = List.of(extensionDescriptorAfterSecureOne, firstExtensionDescriptor, secureExtensionDescriptor); ExtensionDescriptorChainBuilder extensionDescriptorChainBuilder = new ExtensionDescriptorChainBuilder(true); @@ -157,14 +158,14 @@ void testBuildWhenSecureExtensionDescriptorIsExtendedNonStrictMode() { DeploymentDescriptor deploymentDescriptor = buildDeploymentDescriptor(FOO_ID); ExtensionDescriptor firstExtensionDescriptor = buildExtensionDescriptor(BAR_ID, FOO_ID); ExtensionDescriptor secureExtensionDescriptor = buildSecureExtensionDescriptor(BAR_ID); - ExtensionDescriptor extensionDescriptorAfterSecureOne = buildExtensionDescriptor(QUX_ID, "__mta.secure"); + ExtensionDescriptor extensionDescriptorAfterSecureOne = buildExtensionDescriptor(QUX_ID, Constants.SECURE_EXTENSION_DESCRIPTOR_ID); - List extensionDescriptors = Arrays.asList(extensionDescriptorAfterSecureOne, secureExtensionDescriptor, firstExtensionDescriptor); + List extensionDescriptors = List.of(extensionDescriptorAfterSecureOne, secureExtensionDescriptor, firstExtensionDescriptor); ExtensionDescriptorChainBuilder extensionDescriptorChainBuilder = new ExtensionDescriptorChainBuilder(false); List extensionDescriptorChain = extensionDescriptorChainBuilder.build(deploymentDescriptor, extensionDescriptors); - assertEquals(Arrays.asList(firstExtensionDescriptor, secureExtensionDescriptor), extensionDescriptorChain); + assertEquals(List.of(firstExtensionDescriptor, secureExtensionDescriptor), extensionDescriptorChain); } @Test @@ -174,14 +175,14 @@ void testBuildWhenTwoSecureExtensionDescriptorsWhereOnlyTheLastOneGetsAppended() ExtensionDescriptor firstSecureExtensionDescriptor = buildSecureExtensionDescriptor(FOO_ID); ExtensionDescriptor secondSecureExtensionDescriptor = buildSecureExtensionDescriptor(FOO_ID); - List extensionDescriptors = Arrays.asList(firstSecureExtensionDescriptor, firstExtensionDescriptor, secondSecureExtensionDescriptor); + List extensionDescriptors = List.of(firstSecureExtensionDescriptor, firstExtensionDescriptor, secondSecureExtensionDescriptor); ExtensionDescriptorChainBuilder extensionDescriptorChainBuilder = new ExtensionDescriptorChainBuilder(); List extensionDescriptorChain = extensionDescriptorChainBuilder.build(deploymentDescriptor, extensionDescriptors); assertEquals(2, extensionDescriptorChain.size()); assertEquals(firstExtensionDescriptor, extensionDescriptorChain.get(0)); - assertEquals("__mta.secure", extensionDescriptorChain.get(1).getId()); + assertEquals(Constants.SECURE_EXTENSION_DESCRIPTOR_ID, extensionDescriptorChain.get(1).getId()); assertSame(secondSecureExtensionDescriptor, extensionDescriptorChain.get(1)); } @@ -199,7 +200,7 @@ private ExtensionDescriptor buildExtensionDescriptor(String id, String parentId) private ExtensionDescriptor buildSecureExtensionDescriptor(String parentId) { return ExtensionDescriptor.createV2() .setParentId(parentId) - .setId("__mta.secure"); + .setId(Constants.SECURE_EXTENSION_DESCRIPTOR_ID); } } From c6e520cdf3715a0c4f36fa6939aec21500f0768e Mon Sep 17 00:00:00 2001 From: Krasimir Kargov Date: Mon, 16 Feb 2026 13:51:33 +0200 Subject: [PATCH 3/5] Simplifying prune method LMCROSSITXSADEPLOY-2301 --- .../ExtensionDescriptorChainBuilder.java | 65 ++++++++++--------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/builders/ExtensionDescriptorChainBuilder.java b/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/builders/ExtensionDescriptorChainBuilder.java index 6e952bc3..8557c1fb 100644 --- a/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/builders/ExtensionDescriptorChainBuilder.java +++ b/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/builders/ExtensionDescriptorChainBuilder.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -34,6 +35,7 @@ private boolean isSecureDescriptor(ExtensionDescriptor extensionDescriptor) { } public List build(DeploymentDescriptor deploymentDescriptor, List extensionDescriptors) throws ContentException { + saveSecureExtensionDescriptor(extensionDescriptors); Map extensionDescriptorsPerParent = getExtensionDescriptorsPerParent(extensionDescriptors); return build(deploymentDescriptor, extensionDescriptorsPerParent); } @@ -42,25 +44,14 @@ private List build(DeploymentDescriptor deploymentDescripto Map extensionDescriptorsPerParent) { List chain = new ArrayList<>(); Descriptor currentDescriptor = deploymentDescriptor; - ExtensionDescriptor secureDescriptor = null; while (currentDescriptor != null) { ExtensionDescriptor nextDescriptor = extensionDescriptorsPerParent.remove(currentDescriptor.getId()); - - if(nextDescriptor != null && isSecureDescriptor(nextDescriptor)) { - secureDescriptor = nextDescriptor; - continue; - } - CollectionUtils.addIgnoreNull(chain, nextDescriptor); currentDescriptor = nextDescriptor; } - CollectionUtils.addIgnoreNull(chain, secureDescriptor); - - if(secureDescriptor == null && this.secureExtensionDescriptor != null) { - CollectionUtils.addIgnoreNull(chain, this.secureExtensionDescriptor); - } + CollectionUtils.addIgnoreNull(chain, this.secureExtensionDescriptor); if (!extensionDescriptorsPerParent.isEmpty() && isStrict) { throw new ContentException(Messages.CANNOT_BUILD_EXTENSION_DESCRIPTOR_CHAIN_BECAUSE_DESCRIPTORS_0_HAVE_AN_UNKNOWN_PARENT, @@ -77,25 +68,23 @@ private Map getExtensionDescriptorsPerParent(List prune(Map> extensionDescriptorsPerParent) { - return extensionDescriptorsPerParent.entrySet() - .stream() - .collect(Collectors.toMap(Map.Entry::getKey, entry -> { - List localList = entry.getValue(); - - for(ExtensionDescriptor extensionDescriptor : localList) { - if(extensionDescriptor.getId().equals(Constants.SECURE_EXTENSION_DESCRIPTOR_ID)) { - this.secureExtensionDescriptor = extensionDescriptor; - } - } - - for(ExtensionDescriptor extensionDescriptor : localList) { - if(!extensionDescriptor.getId().equals(Constants.SECURE_EXTENSION_DESCRIPTOR_ID)) { - return extensionDescriptor; - } - } - - return localList.get(0); - })); + Map resultMap = new LinkedHashMap<>(); + + for (Map.Entry> entry : extensionDescriptorsPerParent.entrySet()) { + ExtensionDescriptor firstNonSecureExtensionDescriptor = null; + + for (ExtensionDescriptor currentExtensionDescriptorForParent : entry.getValue()) { + if (!isSecureDescriptor(currentExtensionDescriptorForParent)) { + firstNonSecureExtensionDescriptor = currentExtensionDescriptorForParent; + break; + } + } + + if (firstNonSecureExtensionDescriptor != null) { + resultMap.put(entry.getKey(), firstNonSecureExtensionDescriptor); + } + } + return resultMap; } private void validateSingleExtensionDescriptorPerParent(Map> extensionDescriptorsPerParent) { @@ -109,4 +98,16 @@ private void validateSingleExtensionDescriptorPerParent(Map extensionDescriptors) { + ExtensionDescriptor lastSecureExtensionDescriptor = null; + + for(ExtensionDescriptor extensionDescriptor : extensionDescriptors) { + if(isSecureDescriptor(extensionDescriptor)) { + lastSecureExtensionDescriptor = extensionDescriptor; + } + } + + this.secureExtensionDescriptor = lastSecureExtensionDescriptor; + } + +} \ No newline at end of file From 98a062f1669e6783e28c7f096e541fd7d04b0e2e Mon Sep 17 00:00:00 2001 From: Krasimir Kargov Date: Tue, 17 Feb 2026 16:12:25 +0200 Subject: [PATCH 4/5] Enabling misconfigured formatter LMCROSSITXSADEPLOY-2301 --- .../ExtensionDescriptorChainBuilder.java | 34 +++++++----- .../multiapps/mta/model/ExtensionHook.java | 8 +-- .../ExtensionDescriptorChainBuilderTest.java | 53 +++++++++++-------- 3 files changed, 55 insertions(+), 40 deletions(-) diff --git a/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/builders/ExtensionDescriptorChainBuilder.java b/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/builders/ExtensionDescriptorChainBuilder.java index 8557c1fb..bd2364b3 100644 --- a/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/builders/ExtensionDescriptorChainBuilder.java +++ b/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/builders/ExtensionDescriptorChainBuilder.java @@ -1,13 +1,5 @@ package org.cloudfoundry.multiapps.mta.builders; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; - import org.apache.commons.collections4.CollectionUtils; import org.cloudfoundry.multiapps.common.ContentException; import org.cloudfoundry.multiapps.mta.Constants; @@ -16,6 +8,12 @@ import org.cloudfoundry.multiapps.mta.model.Descriptor; import org.cloudfoundry.multiapps.mta.model.ExtensionDescriptor; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + public class ExtensionDescriptorChainBuilder { private final boolean isStrict; @@ -31,10 +29,12 @@ public ExtensionDescriptorChainBuilder(boolean isStrict) { } private boolean isSecureDescriptor(ExtensionDescriptor extensionDescriptor) { - return extensionDescriptor.getId().equals(Constants.SECURE_EXTENSION_DESCRIPTOR_ID); + return extensionDescriptor.getId() + .equals(Constants.SECURE_EXTENSION_DESCRIPTOR_ID); } - public List build(DeploymentDescriptor deploymentDescriptor, List extensionDescriptors) throws ContentException { + public List build(DeploymentDescriptor deploymentDescriptor, List extensionDescriptors) + throws ContentException { saveSecureExtensionDescriptor(extensionDescriptors); Map extensionDescriptorsPerParent = getExtensionDescriptorsPerParent(extensionDescriptors); return build(deploymentDescriptor, extensionDescriptorsPerParent); @@ -62,7 +62,9 @@ private List build(DeploymentDescriptor deploymentDescripto } private Map getExtensionDescriptorsPerParent(List extensionDescriptors) { - Map> extensionDescriptorsPerParent = extensionDescriptors.stream().collect(Collectors.groupingBy(ExtensionDescriptor::getParentId)); + Map> extensionDescriptorsPerParent = extensionDescriptors.stream() + .collect(Collectors.groupingBy( + ExtensionDescriptor::getParentId)); validateSingleExtensionDescriptorPerParent(extensionDescriptorsPerParent); return prune(extensionDescriptorsPerParent); } @@ -91,7 +93,11 @@ private void validateSingleExtensionDescriptorPerParent(Map> extensionDescriptorsForParent : extensionDescriptorsPerParent.entrySet()) { String parent = extensionDescriptorsForParent.getKey(); List extensionDescriptors = extensionDescriptorsForParent.getValue(); - long nonSecureCountOfExtensionDescriptors = extensionDescriptors.stream().filter(descriptor -> !descriptor.getId().equals(Constants.SECURE_EXTENSION_DESCRIPTOR_ID)).count(); + long nonSecureCountOfExtensionDescriptors = extensionDescriptors.stream() + .filter(descriptor -> !descriptor.getId() + .equals( + Constants.SECURE_EXTENSION_DESCRIPTOR_ID)) + .count(); if (nonSecureCountOfExtensionDescriptors > 1 && isStrict) { throw new ContentException(Messages.MULTIPLE_EXTENSION_DESCRIPTORS_EXTEND_THE_PARENT_0, parent); } @@ -101,8 +107,8 @@ private void validateSingleExtensionDescriptorPerParent(Map extensionDescriptors) { ExtensionDescriptor lastSecureExtensionDescriptor = null; - for(ExtensionDescriptor extensionDescriptor : extensionDescriptors) { - if(isSecureDescriptor(extensionDescriptor)) { + for (ExtensionDescriptor extensionDescriptor : extensionDescriptors) { + if (isSecureDescriptor(extensionDescriptor)) { lastSecureExtensionDescriptor = extensionDescriptor; } } diff --git a/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/model/ExtensionHook.java b/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/model/ExtensionHook.java index 401d32cc..21a97e75 100644 --- a/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/model/ExtensionHook.java +++ b/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/model/ExtensionHook.java @@ -1,13 +1,13 @@ package org.cloudfoundry.multiapps.mta.model; -import java.util.Collections; -import java.util.List; -import java.util.Map; - import org.apache.commons.lang3.ObjectUtils; import org.cloudfoundry.multiapps.common.util.yaml.YamlElement; import org.cloudfoundry.multiapps.mta.parsers.v3.ExtensionHookParser; +import java.util.Collections; +import java.util.List; +import java.util.Map; + public class ExtensionHook extends VersionedEntity implements VisitableElement, NamedElement, ParametersContainer { // Required by Jackson. diff --git a/multiapps-mta/src/test/java/org/cloudfoundry/multiapps/mta/builders/ExtensionDescriptorChainBuilderTest.java b/multiapps-mta/src/test/java/org/cloudfoundry/multiapps/mta/builders/ExtensionDescriptorChainBuilderTest.java index b6b49e86..e238e7cb 100644 --- a/multiapps-mta/src/test/java/org/cloudfoundry/multiapps/mta/builders/ExtensionDescriptorChainBuilderTest.java +++ b/multiapps-mta/src/test/java/org/cloudfoundry/multiapps/mta/builders/ExtensionDescriptorChainBuilderTest.java @@ -1,16 +1,5 @@ package org.cloudfoundry.multiapps.mta.builders; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.text.MessageFormat; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - import org.cloudfoundry.multiapps.common.ContentException; import org.cloudfoundry.multiapps.mta.Constants; import org.cloudfoundry.multiapps.mta.Messages; @@ -18,6 +7,15 @@ import org.cloudfoundry.multiapps.mta.model.ExtensionDescriptor; import org.junit.jupiter.api.Test; +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; + class ExtensionDescriptorChainBuilderTest { private static final String FOO_ID = "foo"; @@ -54,8 +52,9 @@ void testBuildWithDescriptorWithUnknownParent() { ContentException contentException = assertThrows(ContentException.class, () -> extensionDescriptorChainBuilder.build(deploymentDescriptor, extensionDescriptors)); - String expectedMessage = MessageFormat.format(Messages.CANNOT_BUILD_EXTENSION_DESCRIPTOR_CHAIN_BECAUSE_DESCRIPTORS_0_HAVE_AN_UNKNOWN_PARENT, - extensionDescriptor2.getId()); + String expectedMessage = MessageFormat.format( + Messages.CANNOT_BUILD_EXTENSION_DESCRIPTOR_CHAIN_BECAUSE_DESCRIPTORS_0_HAVE_AN_UNKNOWN_PARENT, + extensionDescriptor2.getId()); assertEquals(expectedMessage, contentException.getMessage()); } @@ -115,7 +114,8 @@ void testBuildWithNormalAndSecureExtensionDescriptorsWhereSecureIsLastInTheChain List extensionDescriptors = List.of(secureExtensionDescriptor, normalExtensionDescriptor); ExtensionDescriptorChainBuilder extensionDescriptorChainBuilder = new ExtensionDescriptorChainBuilder(); - List extensionDescriptorChain = extensionDescriptorChainBuilder.build(deploymentDescriptor, extensionDescriptors); + List extensionDescriptorChain = extensionDescriptorChainBuilder.build(deploymentDescriptor, + extensionDescriptors); assertEquals(List.of(normalExtensionDescriptor, secureExtensionDescriptor), extensionDescriptorChain); } @@ -128,7 +128,8 @@ void testBuildWithOnlySecureExtensionDescriptor() { List extensionDescriptors = Collections.singletonList(secureExtensionDescriptor); ExtensionDescriptorChainBuilder extensionDescriptorChainBuilder = new ExtensionDescriptorChainBuilder(); - List extensionDescriptorChain = extensionDescriptorChainBuilder.build(deploymentDescriptor, extensionDescriptors); + List extensionDescriptorChain = extensionDescriptorChainBuilder.build(deploymentDescriptor, + extensionDescriptors); assertEquals(Collections.singletonList(secureExtensionDescriptor), extensionDescriptorChain); } @@ -140,11 +141,14 @@ void testBuildWhenSecureExtensionDescriptorIsExtendedStrictMode() { ExtensionDescriptor secureExtensionDescriptor = buildSecureExtensionDescriptor(BAR_ID); ExtensionDescriptor extensionDescriptorAfterSecureOne = buildExtensionDescriptor(QUX_ID, Constants.SECURE_EXTENSION_DESCRIPTOR_ID); - List extensionDescriptors = List.of(extensionDescriptorAfterSecureOne, firstExtensionDescriptor, secureExtensionDescriptor); + List extensionDescriptors = List.of(extensionDescriptorAfterSecureOne, firstExtensionDescriptor, + secureExtensionDescriptor); ExtensionDescriptorChainBuilder extensionDescriptorChainBuilder = new ExtensionDescriptorChainBuilder(true); - ContentException contentException = assertThrows(ContentException.class, () -> extensionDescriptorChainBuilder.build(deploymentDescriptor, extensionDescriptors)); + ContentException contentException = assertThrows(ContentException.class, + () -> extensionDescriptorChainBuilder.build(deploymentDescriptor, + extensionDescriptors)); String expectedMessage = MessageFormat.format( Messages.CANNOT_BUILD_EXTENSION_DESCRIPTOR_CHAIN_BECAUSE_DESCRIPTORS_0_HAVE_AN_UNKNOWN_PARENT, @@ -160,10 +164,12 @@ void testBuildWhenSecureExtensionDescriptorIsExtendedNonStrictMode() { ExtensionDescriptor secureExtensionDescriptor = buildSecureExtensionDescriptor(BAR_ID); ExtensionDescriptor extensionDescriptorAfterSecureOne = buildExtensionDescriptor(QUX_ID, Constants.SECURE_EXTENSION_DESCRIPTOR_ID); - List extensionDescriptors = List.of(extensionDescriptorAfterSecureOne, secureExtensionDescriptor, firstExtensionDescriptor); + List extensionDescriptors = List.of(extensionDescriptorAfterSecureOne, secureExtensionDescriptor, + firstExtensionDescriptor); ExtensionDescriptorChainBuilder extensionDescriptorChainBuilder = new ExtensionDescriptorChainBuilder(false); - List extensionDescriptorChain = extensionDescriptorChainBuilder.build(deploymentDescriptor, extensionDescriptors); + List extensionDescriptorChain = extensionDescriptorChainBuilder.build(deploymentDescriptor, + extensionDescriptors); assertEquals(List.of(firstExtensionDescriptor, secureExtensionDescriptor), extensionDescriptorChain); } @@ -175,14 +181,17 @@ void testBuildWhenTwoSecureExtensionDescriptorsWhereOnlyTheLastOneGetsAppended() ExtensionDescriptor firstSecureExtensionDescriptor = buildSecureExtensionDescriptor(FOO_ID); ExtensionDescriptor secondSecureExtensionDescriptor = buildSecureExtensionDescriptor(FOO_ID); - List extensionDescriptors = List.of(firstSecureExtensionDescriptor, firstExtensionDescriptor, secondSecureExtensionDescriptor); + List extensionDescriptors = List.of(firstSecureExtensionDescriptor, firstExtensionDescriptor, + secondSecureExtensionDescriptor); ExtensionDescriptorChainBuilder extensionDescriptorChainBuilder = new ExtensionDescriptorChainBuilder(); - List extensionDescriptorChain = extensionDescriptorChainBuilder.build(deploymentDescriptor, extensionDescriptors); + List extensionDescriptorChain = extensionDescriptorChainBuilder.build(deploymentDescriptor, + extensionDescriptors); assertEquals(2, extensionDescriptorChain.size()); assertEquals(firstExtensionDescriptor, extensionDescriptorChain.get(0)); - assertEquals(Constants.SECURE_EXTENSION_DESCRIPTOR_ID, extensionDescriptorChain.get(1).getId()); + assertEquals(Constants.SECURE_EXTENSION_DESCRIPTOR_ID, extensionDescriptorChain.get(1) + .getId()); assertSame(secondSecureExtensionDescriptor, extensionDescriptorChain.get(1)); } From 0440e4917e1b95d7b6c569d391e93905a812b55d Mon Sep 17 00:00:00 2001 From: Krasimir Kargov Date: Thu, 19 Feb 2026 01:20:10 +0200 Subject: [PATCH 5/5] Fixing comment about prune method LMCROSSITXSADEPLOY-2301 --- .../mta/builders/ExtensionDescriptorChainBuilder.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/builders/ExtensionDescriptorChainBuilder.java b/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/builders/ExtensionDescriptorChainBuilder.java index bd2364b3..a75e7e24 100644 --- a/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/builders/ExtensionDescriptorChainBuilder.java +++ b/multiapps-mta/src/main/java/org/cloudfoundry/multiapps/mta/builders/ExtensionDescriptorChainBuilder.java @@ -73,18 +73,12 @@ private Map prune(Map resultMap = new LinkedHashMap<>(); for (Map.Entry> entry : extensionDescriptorsPerParent.entrySet()) { - ExtensionDescriptor firstNonSecureExtensionDescriptor = null; - for (ExtensionDescriptor currentExtensionDescriptorForParent : entry.getValue()) { if (!isSecureDescriptor(currentExtensionDescriptorForParent)) { - firstNonSecureExtensionDescriptor = currentExtensionDescriptorForParent; + resultMap.put(entry.getKey(), currentExtensionDescriptorForParent); break; } } - - if (firstNonSecureExtensionDescriptor != null) { - resultMap.put(entry.getKey(), firstNonSecureExtensionDescriptor); - } } return resultMap; }