This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new 3dc8ae26ea Fix bean copy() shallow-copying nested beans in OpenAPI-v3 
DOM (TODO-266)
3dc8ae26ea is described below

commit 3dc8ae26ea53d0b21b3d13209aa8719fd991f572
Author: James Bognar <[email protected]>
AuthorDate: Fri Jul 24 12:35:14 2026 -0400

    Fix bean copy() shallow-copying nested beans in OpenAPI-v3 DOM (TODO-266)
    
    copy() on Components/Operation/PathItem/Callback/Parameter re-wrapped
    Map/List containers via the shallow single-arg overload while sharing the
    nested bean instances, aliasing them between the original and the copy.
    Switched the 15 affected sites to the deep-copy mapper overload
    (Type::copy). Added 5 regression tests asserting nested container
    elements are distinct after copy() and mutations don't cross-leak.
    
    Co-authored-by: Cursor <[email protected]>
---
 .../org/apache/juneau/bean/openapi3/Callback.java  |  2 +-
 .../apache/juneau/bean/openapi3/Components.java    | 18 +++++++-------
 .../org/apache/juneau/bean/openapi3/Operation.java |  4 ++--
 .../org/apache/juneau/bean/openapi3/Parameter.java |  2 +-
 .../org/apache/juneau/bean/openapi3/PathItem.java  |  4 ++--
 .../apache/juneau/bean/openapi3/Callback_Test.java | 12 ++++++++++
 .../juneau/bean/openapi3/Components_Test.java      | 28 ++++++++++++++++++++++
 .../juneau/bean/openapi3/Operation_Test.java       | 14 +++++++++++
 .../juneau/bean/openapi3/Parameter_Test.java       | 12 ++++++++++
 .../apache/juneau/bean/openapi3/PathItem_Test.java | 14 +++++++++++
 10 files changed, 95 insertions(+), 15 deletions(-)

diff --git 
a/juneau-bean/juneau-bean-openapi-v3/src/main/java/org/apache/juneau/bean/openapi3/Callback.java
 
b/juneau-bean/juneau-bean-openapi-v3/src/main/java/org/apache/juneau/bean/openapi3/Callback.java
index 3793eb8835..d472da27a0 100644
--- 
a/juneau-bean/juneau-bean-openapi-v3/src/main/java/org/apache/juneau/bean/openapi3/Callback.java
+++ 
b/juneau-bean/juneau-bean-openapi-v3/src/main/java/org/apache/juneau/bean/openapi3/Callback.java
@@ -96,7 +96,7 @@ public class Callback extends OpenApiElement {
         */
        public Callback(Callback copyFrom) {
                super(copyFrom);
-               this.callbacks = cp(copyFrom.callbacks);
+               this.callbacks = cp(copyFrom.callbacks, PathItem::copy);
        }
 
        /**
diff --git 
a/juneau-bean/juneau-bean-openapi-v3/src/main/java/org/apache/juneau/bean/openapi3/Components.java
 
b/juneau-bean/juneau-bean-openapi-v3/src/main/java/org/apache/juneau/bean/openapi3/Components.java
index 0030547a36..f95e5185c7 100644
--- 
a/juneau-bean/juneau-bean-openapi-v3/src/main/java/org/apache/juneau/bean/openapi3/Components.java
+++ 
b/juneau-bean/juneau-bean-openapi-v3/src/main/java/org/apache/juneau/bean/openapi3/Components.java
@@ -122,15 +122,15 @@ public class Components extends OpenApiElement {
         */
        public Components(Components copyFrom) {
                super(copyFrom);
-               this.schemas = cp(copyFrom.schemas);
-               this.responses = cp(copyFrom.responses);
-               this.parameters = cp(copyFrom.parameters);
-               this.examples = cp(copyFrom.examples);
-               this.requestBodies = cp(copyFrom.requestBodies);
-               this.headers = cp(copyFrom.headers);
-               this.securitySchemes = cp(copyFrom.securitySchemes);
-               this.links = cp(copyFrom.links);
-               this.callbacks = cp(copyFrom.callbacks);
+               this.schemas = cp(copyFrom.schemas, SchemaInfo::copy);
+               this.responses = cp(copyFrom.responses, Response::copy);
+               this.parameters = cp(copyFrom.parameters, Parameter::copy);
+               this.examples = cp(copyFrom.examples, Example::copy);
+               this.requestBodies = cp(copyFrom.requestBodies, 
RequestBodyInfo::copy);
+               this.headers = cp(copyFrom.headers, HeaderInfo::copy);
+               this.securitySchemes = cp(copyFrom.securitySchemes, 
SecuritySchemeInfo::copy);
+               this.links = cp(copyFrom.links, Link::copy);
+               this.callbacks = cp(copyFrom.callbacks, Callback::copy);
        }
 
        /**
diff --git 
a/juneau-bean/juneau-bean-openapi-v3/src/main/java/org/apache/juneau/bean/openapi3/Operation.java
 
b/juneau-bean/juneau-bean-openapi-v3/src/main/java/org/apache/juneau/bean/openapi3/Operation.java
index 3b8a90c7c0..847381ee9a 100644
--- 
a/juneau-bean/juneau-bean-openapi-v3/src/main/java/org/apache/juneau/bean/openapi3/Operation.java
+++ 
b/juneau-bean/juneau-bean-openapi-v3/src/main/java/org/apache/juneau/bean/openapi3/Operation.java
@@ -150,8 +150,8 @@ public class Operation extends OpenApiElement {
                this.externalDocs = copyOf(copyFrom.externalDocs);
                parameters.addAll(copyOf(copyFrom.parameters, Parameter::copy));
                this.requestBody = copyOf(copyFrom.requestBody);
-               responses.putAll(copyFrom.responses);
-               callbacks.putAll(copyFrom.callbacks);
+               responses.putAll(copyOf(copyFrom.responses, Response::copy));
+               callbacks.putAll(copyOf(copyFrom.callbacks, Callback::copy));
                this.deprecated = copyFrom.deprecated;
                security.addAll(copyOf(copyFrom.security, 
SecurityRequirement::copy));
                servers.addAll(copyOf(copyFrom.servers, Server::copy));
diff --git 
a/juneau-bean/juneau-bean-openapi-v3/src/main/java/org/apache/juneau/bean/openapi3/Parameter.java
 
b/juneau-bean/juneau-bean-openapi-v3/src/main/java/org/apache/juneau/bean/openapi3/Parameter.java
index 8af46b9c36..1831707ba5 100644
--- 
a/juneau-bean/juneau-bean-openapi-v3/src/main/java/org/apache/juneau/bean/openapi3/Parameter.java
+++ 
b/juneau-bean/juneau-bean-openapi-v3/src/main/java/org/apache/juneau/bean/openapi3/Parameter.java
@@ -139,7 +139,7 @@ public class Parameter extends OpenApiElement {
                this.allowReserved = copyFrom.allowReserved;
                this.schema = copyOf(copyFrom.schema);
                this.example = copyFrom.example;
-               this.examples = copyOf(copyFrom.examples);
+               this.examples = copyOf(copyFrom.examples, Example::copy);
        }
 
        /**
diff --git 
a/juneau-bean/juneau-bean-openapi-v3/src/main/java/org/apache/juneau/bean/openapi3/PathItem.java
 
b/juneau-bean/juneau-bean-openapi-v3/src/main/java/org/apache/juneau/bean/openapi3/PathItem.java
index 8c3d206a2a..444d074261 100644
--- 
a/juneau-bean/juneau-bean-openapi-v3/src/main/java/org/apache/juneau/bean/openapi3/PathItem.java
+++ 
b/juneau-bean/juneau-bean-openapi-v3/src/main/java/org/apache/juneau/bean/openapi3/PathItem.java
@@ -137,8 +137,8 @@ public class PathItem extends OpenApiElement {
                this.head = copyOf(copyFrom.head);
                this.patch = copyOf(copyFrom.patch);
                this.trace = copyOf(copyFrom.trace);
-               this.servers = copyOf(copyFrom.servers);
-               this.parameters = copyOf(copyFrom.parameters);
+               this.servers = copyOf(copyFrom.servers, Server::copy);
+               this.parameters = copyOf(copyFrom.parameters, Parameter::copy);
        }
 
        /**
diff --git 
a/juneau-bean/juneau-bean-openapi-v3/src/test/java/org/apache/juneau/bean/openapi3/Callback_Test.java
 
b/juneau-bean/juneau-bean-openapi-v3/src/test/java/org/apache/juneau/bean/openapi3/Callback_Test.java
index 3b1c05dfd3..0a1ceedebe 100644
--- 
a/juneau-bean/juneau-bean-openapi-v3/src/test/java/org/apache/juneau/bean/openapi3/Callback_Test.java
+++ 
b/juneau-bean/juneau-bean-openapi-v3/src/test/java/org/apache/juneau/bean/openapi3/Callback_Test.java
@@ -126,6 +126,18 @@ class Callback_Test extends TestBase {
                                "{{{a2}},{{b2}}}"
                        );
                }
+
+               @Test void a14_copyDeepCopiesMapValues() {
+                       var orig = bean()
+                               .addCallback("a1", 
pathItem().setGet(operation().setSummary("orig")));
+
+                       var copy = orig.copy();
+
+                       assertNotSame(orig.getCallbacks().get("a1"), 
copy.getCallbacks().get("a1"));
+
+                       
copy.getCallbacks().get("a1").setGet(operation().setSummary("mutated"));
+                       assertEquals("orig", 
orig.getCallbacks().get("a1").getGet().getSummary());
+               }
        }
 
        @Nested class B_emptyTests extends TestBase {
diff --git 
a/juneau-bean/juneau-bean-openapi-v3/src/test/java/org/apache/juneau/bean/openapi3/Components_Test.java
 
b/juneau-bean/juneau-bean-openapi-v3/src/test/java/org/apache/juneau/bean/openapi3/Components_Test.java
index 9cbc714c3f..7cfe732b5d 100644
--- 
a/juneau-bean/juneau-bean-openapi-v3/src/test/java/org/apache/juneau/bean/openapi3/Components_Test.java
+++ 
b/juneau-bean/juneau-bean-openapi-v3/src/test/java/org/apache/juneau/bean/openapi3/Components_Test.java
@@ -112,6 +112,34 @@ class Components_Test extends TestBase {
 
                        assertDoesNotThrow(() -> bean().set("foo", "bar"));
                }
+
+               @Test void a12_copyDeepCopiesMapValues() {
+                       var orig = bean()
+                               .setSchemas(map("a1", 
schemaInfo().setType("orig")))
+                               .setResponses(map("b1", response("orig")))
+                               .setParameters(map("c1", parameter("orig", 
"orig")))
+                               .setExamples(map("d1", 
example().setSummary("orig")))
+                               .setRequestBodies(map("e1", 
requestBodyInfo().setDescription("orig")))
+                               .setHeaders(map("f1", 
headerInfo(schemaInfo("orig"))))
+                               .setSecuritySchemes(map("g1", 
securitySchemeInfo("orig")))
+                               .setLinks(map("h1", 
link().setOperationId("orig")))
+                               .setCallbacks(map("i1", callback()));
+
+                       var copy = orig.copy();
+
+                       assertNotSame(orig.getSchemas().get("a1"), 
copy.getSchemas().get("a1"));
+                       assertNotSame(orig.getResponses().get("b1"), 
copy.getResponses().get("b1"));
+                       assertNotSame(orig.getParameters().get("c1"), 
copy.getParameters().get("c1"));
+                       assertNotSame(orig.getExamples().get("d1"), 
copy.getExamples().get("d1"));
+                       assertNotSame(orig.getRequestBodies().get("e1"), 
copy.getRequestBodies().get("e1"));
+                       assertNotSame(orig.getHeaders().get("f1"), 
copy.getHeaders().get("f1"));
+                       assertNotSame(orig.getSecuritySchemes().get("g1"), 
copy.getSecuritySchemes().get("g1"));
+                       assertNotSame(orig.getLinks().get("h1"), 
copy.getLinks().get("h1"));
+                       assertNotSame(orig.getCallbacks().get("i1"), 
copy.getCallbacks().get("i1"));
+
+                       copy.getSchemas().get("a1").setType("mutated");
+                       assertEquals("orig", 
orig.getSchemas().get("a1").getType());
+               }
        }
 
        @Nested class B_emptyTests extends TestBase {
diff --git 
a/juneau-bean/juneau-bean-openapi-v3/src/test/java/org/apache/juneau/bean/openapi3/Operation_Test.java
 
b/juneau-bean/juneau-bean-openapi-v3/src/test/java/org/apache/juneau/bean/openapi3/Operation_Test.java
index 1907650a99..2fa1aee390 100644
--- 
a/juneau-bean/juneau-bean-openapi-v3/src/test/java/org/apache/juneau/bean/openapi3/Operation_Test.java
+++ 
b/juneau-bean/juneau-bean-openapi-v3/src/test/java/org/apache/juneau/bean/openapi3/Operation_Test.java
@@ -269,6 +269,20 @@ class Operation_Test extends TestBase {
                                x.setTags(nullTagList);
                        });
                }
+
+               @Test void a18_copyDeepCopiesMapValues() {
+                       var orig = bean()
+                               .setResponses(map("200", 
response().setDescription("orig")))
+                               .addCallback("cb1", callback());
+
+                       var copy = orig.copy();
+
+                       assertNotSame(orig.getResponses().get("200"), 
copy.getResponses().get("200"));
+                       assertNotSame(orig.getCallbacks().get("cb1"), 
copy.getCallbacks().get("cb1"));
+
+                       
copy.getResponses().get("200").setDescription("mutated");
+                       assertEquals("orig", 
orig.getResponses().get("200").getDescription());
+               }
        }
 
        @Nested class B_emptyTests extends TestBase {
diff --git 
a/juneau-bean/juneau-bean-openapi-v3/src/test/java/org/apache/juneau/bean/openapi3/Parameter_Test.java
 
b/juneau-bean/juneau-bean-openapi-v3/src/test/java/org/apache/juneau/bean/openapi3/Parameter_Test.java
index c66398607c..81d95261ed 100644
--- 
a/juneau-bean/juneau-bean-openapi-v3/src/test/java/org/apache/juneau/bean/openapi3/Parameter_Test.java
+++ 
b/juneau-bean/juneau-bean-openapi-v3/src/test/java/org/apache/juneau/bean/openapi3/Parameter_Test.java
@@ -136,6 +136,18 @@ class Parameter_Test extends TestBase {
                        assertDoesNotThrow(() -> x.setStyle("deepObject"));
                        assertDoesNotThrow(() -> y.setStyle("invalid"));
                }
+
+               @Test void a12_copyDeepCopiesMapValues() {
+                       var orig = bean()
+                               .setExamples(map("a1", 
example().setSummary("orig")));
+
+                       var copy = orig.copy();
+
+                       assertNotSame(orig.getExamples().get("a1"), 
copy.getExamples().get("a1"));
+
+                       copy.getExamples().get("a1").setSummary("mutated");
+                       assertEquals("orig", 
orig.getExamples().get("a1").getSummary());
+               }
        }
 
        @Nested class B_emptyTests extends TestBase {
diff --git 
a/juneau-bean/juneau-bean-openapi-v3/src/test/java/org/apache/juneau/bean/openapi3/PathItem_Test.java
 
b/juneau-bean/juneau-bean-openapi-v3/src/test/java/org/apache/juneau/bean/openapi3/PathItem_Test.java
index 7e03aa56d0..949aeab9c6 100644
--- 
a/juneau-bean/juneau-bean-openapi-v3/src/test/java/org/apache/juneau/bean/openapi3/PathItem_Test.java
+++ 
b/juneau-bean/juneau-bean-openapi-v3/src/test/java/org/apache/juneau/bean/openapi3/PathItem_Test.java
@@ -111,6 +111,20 @@ class PathItem_Test extends TestBase {
                        assertTrue(bean().strict().isStrict());
                        assertFalse(bean().strict(false).isStrict());
                }
+
+               @Test void a12_copyDeepCopiesListValues() {
+                       var orig = bean()
+                               
.setServers(l(server().setUrl(URI.create("http://example.com";))))
+                               
.setParameters(l(parameter().setIn("q").setName("orig")));
+
+                       var copy = orig.copy();
+
+                       assertNotSame(orig.getServers().get(0), 
copy.getServers().get(0));
+                       assertNotSame(orig.getParameters().get(0), 
copy.getParameters().get(0));
+
+                       copy.getParameters().get(0).setName("mutated");
+                       assertEquals("orig", 
orig.getParameters().get(0).getName());
+               }
        }
 
        @Nested class B_emptyTests extends TestBase {

Reply via email to