diff --git a/solr/test-framework/src/java/org/apache/solr/embedded/JettyConfig.java b/solr/test-framework/src/java/org/apache/solr/embedded/JettyConfig.java index 20516d7c007..258657bedbc 100644 --- a/solr/test-framework/src/java/org/apache/solr/embedded/JettyConfig.java +++ b/solr/test-framework/src/java/org/apache/solr/embedded/JettyConfig.java @@ -17,6 +17,7 @@ package org.apache.solr.embedded; import jakarta.servlet.Filter; +import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import java.util.TreeMap; @@ -40,29 +41,22 @@ public class JettyConfig { /** If true, serve the Admin UI static files and index.html like the production web.xml does. */ public final boolean enableAdminUi; - private JettyConfig( - boolean onlyHttp1, - int port, - int portRetryTime, - boolean stopAtShutdown, - Long waitForLoadingCoresToFinishMs, - Map extraServlets, - Map, String> extraFilters, - SSLConfig sslConfig, - boolean enableV2, - boolean enableGracefulShutdown, - boolean enableAdminUi) { - this.onlyHttp1 = onlyHttp1; - this.port = port; - this.portRetryTime = portRetryTime; - this.stopAtShutdown = stopAtShutdown; - this.waitForLoadingCoresToFinishMs = waitForLoadingCoresToFinishMs; - this.extraServlets = extraServlets; - this.extraFilters = extraFilters; - this.sslConfig = sslConfig; - this.enableV2 = enableV2; - this.enableGracefulShutdown = enableGracefulShutdown; - this.enableAdminUi = enableAdminUi; + /** Snapshot of the builder that built this config; enables {@link #builder(JettyConfig)}. */ + private final Builder builder; + + private JettyConfig(Builder builder) { + this.builder = builder; + this.onlyHttp1 = builder.onlyHttp1; + this.port = builder.port; + this.portRetryTime = builder.portRetryTime; + this.stopAtShutdown = builder.stopAtShutdown; + this.waitForLoadingCoresToFinishMs = builder.waitForLoadingCoresToFinishMs; + this.extraServlets = Collections.unmodifiableMap(builder.extraServlets); + this.extraFilters = Collections.unmodifiableMap(builder.extraFilters); + this.sslConfig = builder.sslConfig; + this.enableV2 = builder.enableV2; + this.enableGracefulShutdown = builder.enableGracefulShutdown; + this.enableAdminUi = builder.enableAdminUi; } public static Builder builder() { @@ -70,23 +64,10 @@ public static Builder builder() { } public static Builder builder(JettyConfig other) { - Builder builder = new Builder(); - - builder.onlyHttp1 = other.onlyHttp1; - builder.port = other.port; - builder.portRetryTime = other.portRetryTime; - builder.stopAtShutdown = other.stopAtShutdown; - builder.waitForLoadingCoresToFinishMs = other.waitForLoadingCoresToFinishMs; - builder.extraServlets = other.extraServlets; - builder.extraFilters = other.extraFilters; - builder.sslConfig = other.sslConfig; - builder.enableV2 = other.enableV2; - builder.enableGracefulShutdown = other.enableGracefulShutdown; - builder.enableAdminUi = other.enableAdminUi; - return builder; + return other.builder.clone(); } - public static class Builder { + public static class Builder implements Cloneable { boolean onlyHttp1 = false; int port = 0; @@ -166,19 +147,22 @@ public Builder withPortRetryTime(int portRetryTime) { return this; } + /** Copies the maps too, so the clone is fully independent; the SSLConfig is shared. */ + @Override + public Builder clone() { + try { + Builder clone = (Builder) super.clone(); + clone.extraServlets = new TreeMap<>(extraServlets); + clone.extraFilters = new LinkedHashMap<>(extraFilters); + return clone; + } catch (CloneNotSupportedException e) { + throw new AssertionError(e); + } + } + public JettyConfig build() { - return new JettyConfig( - onlyHttp1, - port, - portRetryTime, - stopAtShutdown, - waitForLoadingCoresToFinishMs, - extraServlets, - extraFilters, - sslConfig, - enableV2, - enableGracefulShutdown, - enableAdminUi); + // clone so later mutations of this builder don't leak into the built config's snapshot + return new JettyConfig(clone()); } } }