You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When deserializing TlsContextConfiguration from YAML using Jackson, field default values are not respected and the deserialized object contains the default value for that type of field (null) for reference types, and the default value for primitive types (e.g., false for boolean).
The tests have always used SnakeYAML to deserialize, and they've always passed. And in all of our Dropwizard services, we use dropwizard-config-providers to provide default values for key/trust stores and thus we don't have explicit configuration for TlsContextConfiguration in our YAML.
But recently for some new development work, I needed to have an explicit configuration for TlsContextConfiguration in a YAML configuration for a Dropwizard application, and found that it was not respecting the default values for keyStoreType and trustStoreType (JKS), for protocol (TLSv1.2), and for verifyHostname (true). Instead, keyStoreType, trustStoreType, and protocol were all null and verifyHostname was false. And yet all the tests pass in TlsContextConfigurationTest! (I guess tests are worthless after all 🤣 )
Long story short, it was because of the Lombok annotations used in TlsContextConfiguration.
TlsContextConfiguration has the following Lombok annotations at the class-level:
@Getter@Setter@Builder@NoArgsConstructor@AllArgsConstructor(access = AccessLevel.PRIVATE) // for Builder (b/c also need no-args constructor)
It also uses @Builder.Default on protocol, keyStoreType, trustStoreType, and verifyHostname. Over time we've found that this combination of Lombok annotations does not work as you would expect in some situations, depending on whether you use the builder, the no-args constructor, or the all-args constructor. The builder and no-args constructor work as expected, while the all-args constructor does not.
But why did the tests pass using SnakeYAML, and why did the deserialization not work as expected in the Dropwizard application? When I de-lomboked TlsContextConfiguration, the fields each still have their default value, so when the no-args constructor is used along with setter methods, the default values are respected unless overwritten. But the all-args constructor does not respect the default values. Here is the de-lomboked all-args constructor code:
When I ran the tests through the debugger, I found that SnakeYAML uses the no-args constructor and then calls setter methods, while Jackson (which is what Dropwizard uses under the hood), uses the all-args constructor. When Jackson parses YAML that does not contain values for all properties, it passes default values to the all-args constructor, e.g., null for reference types and false for primitive boolean. This is why the fields with default values are incorrect in the deserialized object when using Jackson. I'm sure there are some ways to configure Jackson to force it to use the no-args constructor but the easier solution is to simply remove the top-level Lombok constructor annotations and the @Builder.Default ones, and write the constructors manually. This way the all-args constructor can respect the field default values.
The text was updated successfully, but these errors were encountered:
* Replace Lombok constructor annotations with actual constructors
such that the all-args constructor respects the default values.
* Remove the Builder.Default methods since the default values
are now handled in the all-args constructor.
* The no-args constructor calls the all-args constructor with
all null values so that defaults are in one place (instead of
on field declarations and in the all-args constructor).
* The all-args constructor sets the appropriate default values
when it does not receive a value for a given field. It also
has the ConstructorProperties annotation to help deserializers
know how the arguments map to fields.
* Update tests for TlsContextConfiguration and
SecureEndpointsConfiguration so that deserialization from YAML is
tested for both TlsContextConfiguration and SSLContextConfiguration
(SecureEndpointsConfiguration extends it) using SnakeYAML, Jackson,
and Dropwizard. Though Dropwizard uses Jackson and should be the
same as using "plain Jackson," we want to make 100% sure Dropwizard
isn't doing any kind of customization in Jackson that might change
how it deserializes the YAML.
* Add new helper methods in YamlTestHelper and rename the existing
method so that the method names clearly express how they perform
the deserialization, i.e., using SnakeYAML or Jackson.
Closes#1209
* Replace Lombok constructor annotations with actual constructors such
that the all-args constructor respects the default values.
* Remove the Builder.Default methods since the default values are now
handled in the all-args constructor.
* The no-args constructor calls the all-args constructor with all null
values so that defaults are in one place (instead of on field
declarations and in the all-args constructor).
* The all-args constructor sets the appropriate default values when it
does not receive a value for a given field. It also has the
ConstructorProperties annotation to help deserializers know how the
arguments map to fields.
* Update tests for TlsContextConfiguration and
SecureEndpointsConfiguration so that deserialization from YAML is tested
for both TlsContextConfiguration and SSLContextConfiguration
(SecureEndpointsConfiguration extends it) using SnakeYAML, Jackson, and
Dropwizard. Though Dropwizard uses Jackson and should be the same as
using "plain Jackson," we want to make 100% sure Dropwizard isn't doing
any kind of customization in Jackson that might change how it
deserializes the YAML.
* Add new helper methods in YamlTestHelper and rename the existing
method so that the method names clearly express how they perform the
deserialization, i.e., using SnakeYAML or Jackson.
Closes#1209
When deserializing
TlsContextConfiguration
from YAML using Jackson, field default values are not respected and the deserialized object contains the default value for that type of field (null
) for reference types, and the default value for primitive types (e.g.,false
forboolean
).The tests have always used SnakeYAML to deserialize, and they've always passed. And in all of our Dropwizard services, we use dropwizard-config-providers to provide default values for key/trust stores and thus we don't have explicit configuration for
TlsContextConfiguration
in our YAML.But recently for some new development work, I needed to have an explicit configuration for
TlsContextConfiguration
in a YAML configuration for a Dropwizard application, and found that it was not respecting the default values forkeyStoreType
andtrustStoreType
(JKS
), forprotocol
(TLSv1.2
), and forverifyHostname
(true
). Instead,keyStoreType
,trustStoreType
, andprotocol
were allnull
andverifyHostname
wasfalse
. And yet all the tests pass inTlsContextConfigurationTest
! (I guess tests are worthless after all 🤣 )Long story short, it was because of the Lombok annotations used in
TlsContextConfiguration
.TlsContextConfiguration
has the following Lombok annotations at the class-level:It also uses
@Builder.Default
onprotocol
,keyStoreType
,trustStoreType
, andverifyHostname
. Over time we've found that this combination of Lombok annotations does not work as you would expect in some situations, depending on whether you use the builder, the no-args constructor, or the all-args constructor. The builder and no-args constructor work as expected, while the all-args constructor does not.But why did the tests pass using SnakeYAML, and why did the deserialization not work as expected in the Dropwizard application? When I de-lomboked
TlsContextConfiguration
, the fields each still have their default value, so when the no-args constructor is used along with setter methods, the default values are respected unless overwritten. But the all-args constructor does not respect the default values. Here is the de-lomboked all-args constructor code:When I ran the tests through the debugger, I found that SnakeYAML uses the no-args constructor and then calls setter methods, while Jackson (which is what Dropwizard uses under the hood), uses the all-args constructor. When Jackson parses YAML that does not contain values for all properties, it passes default values to the all-args constructor, e.g.,
null
for reference types andfalse
for primitiveboolean
. This is why the fields with default values are incorrect in the deserialized object when using Jackson. I'm sure there are some ways to configure Jackson to force it to use the no-args constructor but the easier solution is to simply remove the top-level Lombok constructor annotations and the@Builder.Default
ones, and write the constructors manually. This way the all-args constructor can respect the field default values.The text was updated successfully, but these errors were encountered: