diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationEndpointConfigurer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationEndpointConfigurer.java
index 2dff06b6..cde3c8ae 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationEndpointConfigurer.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationEndpointConfigurer.java
@@ -130,7 +130,7 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C
*
*
* - It must be an HTTP POST
- * - It must be submitted to {@link ProviderSettings#authorizationEndpoint()}
+ * - It must be submitted to {@link ProviderSettings#getAuthorizationEndpoint()} ()}
* - It must include the received {@code client_id} as an HTTP parameter
* - It must include the received {@code state} as an HTTP parameter
* - It must include the list of {@code scope}s the {@code Resource Owner}
@@ -150,10 +150,10 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C
ProviderSettings providerSettings = OAuth2ConfigurerUtils.getProviderSettings(builder);
this.requestMatcher = new OrRequestMatcher(
new AntPathRequestMatcher(
- providerSettings.authorizationEndpoint(),
+ providerSettings.getAuthorizationEndpoint(),
HttpMethod.GET.name()),
new AntPathRequestMatcher(
- providerSettings.authorizationEndpoint(),
+ providerSettings.getAuthorizationEndpoint(),
HttpMethod.POST.name()));
List authenticationProviders =
@@ -172,7 +172,7 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C
OAuth2AuthorizationEndpointFilter authorizationEndpointFilter =
new OAuth2AuthorizationEndpointFilter(
authenticationManager,
- providerSettings.authorizationEndpoint());
+ providerSettings.getAuthorizationEndpoint());
if (this.authorizationRequestConverter != null) {
authorizationEndpointFilter.setAuthenticationConverter(this.authorizationRequestConverter);
}
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationServerConfigurer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationServerConfigurer.java
index 4e082761..355a9ed6 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationServerConfigurer.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationServerConfigurer.java
@@ -227,7 +227,7 @@ public final class OAuth2AuthorizationServerConfigurer configurer.configure(builder));
ProviderSettings providerSettings = OAuth2ConfigurerUtils.getProviderSettings(builder);
- if (providerSettings.issuer() != null) {
+ if (providerSettings.getIssuer() != null) {
OidcProviderConfigurationEndpointFilter oidcProviderConfigurationEndpointFilter =
new OidcProviderConfigurationEndpointFilter(providerSettings);
builder.addFilterBefore(postProcess(oidcProviderConfigurationEndpointFilter), AbstractPreAuthenticatedProcessingFilter.class);
@@ -240,7 +240,7 @@ public final class OAuth2AuthorizationServerConfigurer jwkSource = OAuth2ConfigurerUtils.getJwkSource(builder);
NimbusJwkSetEndpointFilter jwkSetEndpointFilter = new NimbusJwkSetEndpointFilter(
jwkSource,
- providerSettings.jwkSetEndpoint());
+ providerSettings.getJwkSetEndpoint());
builder.addFilterBefore(postProcess(jwkSetEndpointFilter), AbstractPreAuthenticatedProcessingFilter.class);
AuthenticationManager authenticationManager = builder.getSharedObject(AuthenticationManager.class);
@@ -257,20 +257,20 @@ public final class OAuth2AuthorizationServerConfigurer> void init(B builder) {
ProviderSettings providerSettings = OAuth2ConfigurerUtils.getProviderSettings(builder);
this.requestMatcher = new AntPathRequestMatcher(
- providerSettings.tokenEndpoint(), HttpMethod.POST.name());
+ providerSettings.getTokenEndpoint(), HttpMethod.POST.name());
List authenticationProviders =
!this.authenticationProviders.isEmpty() ?
@@ -137,7 +137,7 @@ public final class OAuth2TokenEndpointConfigurer extends AbstractOAuth2Configure
OAuth2TokenEndpointFilter tokenEndpointFilter =
new OAuth2TokenEndpointFilter(
authenticationManager,
- providerSettings.tokenEndpoint());
+ providerSettings.getTokenEndpoint());
if (this.accessTokenRequestConverter != null) {
tokenEndpointFilter.setAuthenticationConverter(this.accessTokenRequestConverter);
}
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/JwtUtils.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/JwtUtils.java
index f8d8dc52..8d72001e 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/JwtUtils.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/JwtUtils.java
@@ -50,7 +50,7 @@ final class JwtUtils {
String issuer, String subject, Set authorizedScopes) {
Instant issuedAt = Instant.now();
- Instant expiresAt = issuedAt.plus(registeredClient.getTokenSettings().accessTokenTimeToLive());
+ Instant expiresAt = issuedAt.plus(registeredClient.getTokenSettings().getAccessTokenTimeToLive());
// @formatter:off
JwtClaimsSet.Builder claimsBuilder = JwtClaimsSet.builder();
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProvider.java
index 8d90240a..6e0e283b 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProvider.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProvider.java
@@ -140,7 +140,7 @@ public final class OAuth2AuthorizationCodeAuthenticationProvider implements Auth
throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_GRANT));
}
- String issuer = this.providerSettings != null ? this.providerSettings.issuer() : null;
+ String issuer = this.providerSettings != null ? this.providerSettings.getIssuer() : null;
Set authorizedScopes = authorization.getAttribute(
OAuth2Authorization.AUTHORIZED_SCOPE_ATTRIBUTE_NAME);
@@ -174,7 +174,7 @@ public final class OAuth2AuthorizationCodeAuthenticationProvider implements Auth
OAuth2RefreshToken refreshToken = null;
if (registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.REFRESH_TOKEN)) {
refreshToken = OAuth2RefreshTokenAuthenticationProvider.generateRefreshToken(
- registeredClient.getTokenSettings().refreshTokenTimeToLive());
+ registeredClient.getTokenSettings().getRefreshTokenTimeToLive());
}
Jwt jwtIdToken = null;
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationProvider.java
index 07fcefbd..2357ef6f 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationProvider.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationProvider.java
@@ -151,7 +151,7 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen
authorizationCodeRequestAuthentication, registeredClient, null);
}
}
- } else if (registeredClient.getClientSettings().requireProofKey()) {
+ } else if (registeredClient.getClientSettings().isRequireProofKey()) {
throwError(OAuth2ErrorCodes.INVALID_REQUEST, PkceParameterNames.CODE_CHALLENGE, PKCE_ERROR_URI,
authorizationCodeRequestAuthentication, registeredClient, null);
}
@@ -341,7 +341,7 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen
private static boolean requireAuthorizationConsent(RegisteredClient registeredClient,
OAuth2AuthorizationRequest authorizationRequest, OAuth2AuthorizationConsent authorizationConsent) {
- if (!registeredClient.getClientSettings().requireAuthorizationConsent()) {
+ if (!registeredClient.getClientSettings().isRequireAuthorizationConsent()) {
return false;
}
// 'openid' scope does not require consent
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientAuthenticationProvider.java
index 39ed684d..9d642a60 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientAuthenticationProvider.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientAuthenticationProvider.java
@@ -150,7 +150,7 @@ public final class OAuth2ClientAuthenticationProvider implements AuthenticationP
String codeChallenge = (String) authorizationRequest.getAdditionalParameters()
.get(PkceParameterNames.CODE_CHALLENGE);
if (!StringUtils.hasText(codeChallenge) &&
- registeredClient.getClientSettings().requireProofKey()) {
+ registeredClient.getClientSettings().isRequireProofKey()) {
throwInvalidClient();
}
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProvider.java
index 96134f30..758d6e04 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProvider.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProvider.java
@@ -113,7 +113,7 @@ public final class OAuth2ClientCredentialsAuthenticationProvider implements Auth
authorizedScopes = new LinkedHashSet<>(clientCredentialsAuthentication.getScopes());
}
- String issuer = this.providerSettings != null ? this.providerSettings.issuer() : null;
+ String issuer = this.providerSettings != null ? this.providerSettings.getIssuer() : null;
JoseHeader.Builder headersBuilder = JwtUtils.headers();
JwtClaimsSet.Builder claimsBuilder = JwtUtils.accessTokenClaims(
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProvider.java
index 37d8a774..685bde98 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProvider.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProvider.java
@@ -147,7 +147,7 @@ public final class OAuth2RefreshTokenAuthenticationProvider implements Authentic
scopes = authorizedScopes;
}
- String issuer = this.providerSettings != null ? this.providerSettings.issuer() : null;
+ String issuer = this.providerSettings != null ? this.providerSettings.getIssuer() : null;
JoseHeader.Builder headersBuilder = JwtUtils.headers();
JwtClaimsSet.Builder claimsBuilder = JwtUtils.accessTokenClaims(
@@ -178,8 +178,8 @@ public final class OAuth2RefreshTokenAuthenticationProvider implements Authentic
TokenSettings tokenSettings = registeredClient.getTokenSettings();
OAuth2RefreshToken currentRefreshToken = refreshToken.getToken();
- if (!tokenSettings.reuseRefreshTokens()) {
- currentRefreshToken = generateRefreshToken(tokenSettings.refreshTokenTimeToLive());
+ if (!tokenSettings.isReuseRefreshTokens()) {
+ currentRefreshToken = generateRefreshToken(tokenSettings.getRefreshTokenTimeToLive());
}
Jwt jwtIdToken = null;
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/JdbcRegisteredClientRepository.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/JdbcRegisteredClientRepository.java
index c9e8d893..e1371586 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/JdbcRegisteredClientRepository.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/JdbcRegisteredClientRepository.java
@@ -39,6 +39,8 @@ import org.springframework.jdbc.core.SqlParameterValue;
import org.springframework.security.jackson2.SecurityJackson2Modules;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
+import org.springframework.security.oauth2.server.authorization.config.ClientSettings;
+import org.springframework.security.oauth2.server.authorization.config.TokenSettings;
import org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -211,12 +213,10 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor
// @formatter:on
Map clientSettingsMap = parseMap(rs.getString("client_settings"));
- builder.clientSettings(clientSettings ->
- clientSettings.settings().putAll(clientSettingsMap));
+ builder.clientSettings(ClientSettings.withSettings(clientSettingsMap).build());
Map tokenSettingsMap = parseMap(rs.getString("token_settings"));
- builder.tokenSettings(tokenSettings ->
- tokenSettings.settings().putAll(tokenSettingsMap));
+ builder.tokenSettings(TokenSettings.withSettings(tokenSettingsMap).build());
return builder.build();
}
@@ -303,8 +303,8 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor
new SqlParameterValue(Types.VARCHAR, StringUtils.collectionToCommaDelimitedString(authorizationGrantTypes)),
new SqlParameterValue(Types.VARCHAR, StringUtils.collectionToCommaDelimitedString(registeredClient.getRedirectUris())),
new SqlParameterValue(Types.VARCHAR, StringUtils.collectionToCommaDelimitedString(registeredClient.getScopes())),
- new SqlParameterValue(Types.VARCHAR, writeMap(registeredClient.getClientSettings().settings())),
- new SqlParameterValue(Types.VARCHAR, writeMap(registeredClient.getTokenSettings().settings())));
+ new SqlParameterValue(Types.VARCHAR, writeMap(registeredClient.getClientSettings().getSettings())),
+ new SqlParameterValue(Types.VARCHAR, writeMap(registeredClient.getTokenSettings().getSettings())));
}
public final void setObjectMapper(ObjectMapper objectMapper) {
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/RegisteredClient.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/RegisteredClient.java
index 1e1715c4..94ff1ba2 100644
--- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/RegisteredClient.java
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/RegisteredClient.java
@@ -190,15 +190,15 @@ public class RegisteredClient implements Serializable {
Objects.equals(this.authorizationGrantTypes, that.authorizationGrantTypes) &&
Objects.equals(this.redirectUris, that.redirectUris) &&
Objects.equals(this.scopes, that.scopes) &&
- Objects.equals(this.clientSettings.settings(), that.getClientSettings().settings()) &&
- Objects.equals(this.tokenSettings.settings(), that.tokenSettings.settings());
+ Objects.equals(this.clientSettings, that.clientSettings) &&
+ Objects.equals(this.tokenSettings, that.tokenSettings);
}
@Override
public int hashCode() {
return Objects.hash(this.id, this.clientId, this.clientIdIssuedAt, this.clientSecret, this.clientSecretExpiresAt,
this.clientName, this.clientAuthenticationMethods, this.authorizationGrantTypes, this.redirectUris,
- this.scopes, this.clientSettings.settings(), this.tokenSettings.settings());
+ this.scopes, this.clientSettings, this.tokenSettings);
}
@Override
@@ -211,8 +211,8 @@ public class RegisteredClient implements Serializable {
", authorizationGrantTypes=" + this.authorizationGrantTypes +
", redirectUris=" + this.redirectUris +
", scopes=" + this.scopes +
- ", clientSettings=" + this.clientSettings.settings() +
- ", tokenSettings=" + this.tokenSettings.settings() +
+ ", clientSettings=" + this.clientSettings +
+ ", tokenSettings=" + this.tokenSettings +
'}';
}
@@ -253,8 +253,8 @@ public class RegisteredClient implements Serializable {
private Set authorizationGrantTypes = new HashSet<>();
private Set redirectUris = new HashSet<>();
private Set scopes = new HashSet<>();
- private ClientSettings clientSettings = new ClientSettings();
- private TokenSettings tokenSettings = new TokenSettings();
+ private ClientSettings clientSettings;
+ private TokenSettings tokenSettings;
protected Builder(String id) {
this.id = id;
@@ -279,8 +279,8 @@ public class RegisteredClient implements Serializable {
if (!CollectionUtils.isEmpty(registeredClient.scopes)) {
this.scopes.addAll(registeredClient.scopes);
}
- this.clientSettings = new ClientSettings(registeredClient.clientSettings.settings());
- this.tokenSettings = new TokenSettings(registeredClient.tokenSettings.settings());
+ this.clientSettings = ClientSettings.withSettings(registeredClient.getClientSettings().getSettings()).build();
+ this.tokenSettings = TokenSettings.withSettings(registeredClient.getTokenSettings().getSettings()).build();
}
/**
@@ -444,26 +444,24 @@ public class RegisteredClient implements Serializable {
}
/**
- * A {@link Consumer} of the client configuration settings,
- * allowing the ability to add, replace, or remove.
+ * Sets the {@link ClientSettings client configuration settings}.
*
- * @param clientSettingsConsumer a {@link Consumer} of the client configuration settings
+ * @param clientSettings the client configuration settings
* @return the {@link Builder}
*/
- public Builder clientSettings(Consumer clientSettingsConsumer) {
- clientSettingsConsumer.accept(this.clientSettings);
+ public Builder clientSettings(ClientSettings clientSettings) {
+ this.clientSettings = clientSettings;
return this;
}
/**
- * A {@link Consumer} of the token configuration settings,
- * allowing the ability to add, replace, or remove.
+ * Sets the {@link TokenSettings token configuration settings}.
*
- * @param tokenSettingsConsumer a {@link Consumer} of the token configuration settings
+ * @param tokenSettings the token configuration settings
* @return the {@link Builder}
*/
- public Builder tokenSettings(Consumer tokenSettingsConsumer) {
- tokenSettingsConsumer.accept(this.tokenSettings);
+ public Builder tokenSettings(TokenSettings tokenSettings) {
+ this.tokenSettings = tokenSettings;
return this;
}
@@ -506,8 +504,10 @@ public class RegisteredClient implements Serializable {
new HashSet<>(this.redirectUris));
registeredClient.scopes = Collections.unmodifiableSet(
new HashSet<>(this.scopes));
- registeredClient.clientSettings = new ClientSettings(this.clientSettings.settings());
- registeredClient.tokenSettings = new TokenSettings(this.tokenSettings.settings());
+ registeredClient.clientSettings = this.clientSettings != null ?
+ this.clientSettings : ClientSettings.builder().build();
+ registeredClient.tokenSettings = this.tokenSettings != null ?
+ this.tokenSettings : TokenSettings.builder().build();
return registeredClient;
}
diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/AbstractSettings.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/AbstractSettings.java
new file mode 100644
index 00000000..56fbdfcd
--- /dev/null
+++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/AbstractSettings.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2020-2021 the original author or authors.
+ *
+ * Licensed 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
+ *
+ * https://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.
+ */
+package org.springframework.security.oauth2.server.authorization.config;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Consumer;
+
+import org.springframework.security.oauth2.core.Version;
+import org.springframework.util.Assert;
+
+/**
+ * Base implementation for configuration settings.
+ *
+ * @author Joe Grandja
+ * @since 0.0.2
+ */
+public abstract class AbstractSettings implements Serializable {
+ private static final long serialVersionUID = Version.SERIAL_VERSION_UID;
+ private final Map settings;
+
+ protected AbstractSettings(Map settings) {
+ Assert.notEmpty(settings, "settings cannot be empty");
+ this.settings = Collections.unmodifiableMap(new HashMap<>(settings));
+ }
+
+ /**
+ * Returns a configuration setting.
+ *
+ * @param name the name of the setting
+ * @param the type of the setting
+ * @return the value of the setting, or {@code null} if not available
+ */
+ @SuppressWarnings("unchecked")
+ public T getSetting(String name) {
+ Assert.hasText(name, "name cannot be empty");
+ return (T) getSettings().get(name);
+ }
+
+ /**
+ * Returns a {@code Map} of the configuration settings.
+ *
+ * @return a {@code Map} of the configuration settings
+ */
+ public Map getSettings() {
+ return this.settings;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ AbstractSettings that = (AbstractSettings) obj;
+ return this.settings.equals(that.settings);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(this.settings);
+ }
+
+ @Override
+ public String toString() {
+ return "AbstractSettings {" +
+ "settings=" + this.settings +
+ '}';
+ }
+
+ /**
+ * A builder for subclasses of {@link AbstractSettings}.
+ */
+ protected static abstract class AbstractBuilder> {
+ private final Map settings = new HashMap<>();
+
+ protected AbstractBuilder() {
+ }
+
+ /**
+ * Sets a configuration setting.
+ *
+ * @param name the name of the setting
+ * @param value the value of the setting
+ * @return the {@link AbstractBuilder} for further configuration
+ */
+ public B setting(String name, Object value) {
+ Assert.hasText(name, "name cannot be empty");
+ Assert.notNull(value, "value cannot be null");
+ getSettings().put(name, value);
+ return getThis();
+ }
+
+ /**
+ * A {@code Consumer} of the configuration settings {@code Map}
+ * allowing the ability to add, replace, or remove.
+ *
+ * @param settingsConsumer a {@link Consumer} of the configuration settings {@code Map}
+ * @return the {@link AbstractBuilder} for further configuration
+ */
+ public B settings(Consumer