Skip to content

Commit

Permalink
fix(model): Use SettableBeanProperty.Delegating for jackson delegation
Browse files Browse the repository at this point in the history
Changes the delegation in SettableBeanPropertyDelegate from
a custom implementation to the standard way of implementing
a delegating property in jackson. This way, if some jackson
module overrides methods that are not delegated explicitely
here, they will continue to work.

Fixes: fabric8io#6342
  • Loading branch information
fxshlein committed Sep 11, 2024
1 parent 32b3473 commit c21f267
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 73 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#### Bugs
* Fix #6038: Support for Gradle configuration cache
* Fix #6214: Java generator does not recognize fields in CRDs other than metadata, spec, and status
* Fix #6342: UnmatchedFieldTypeModule prevents certain jackson features from working

#### Improvements
* Fix #5264: Remove deprecated `Config.errorMessages` field
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,12 @@
package io.fabric8.kubernetes.model.jackson;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.PropertyName;
import com.fasterxml.jackson.databind.deser.NullValueProvider;
import com.fasterxml.jackson.databind.deser.SettableAnyProperty;
import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.function.BooleanSupplier;

/**
Expand All @@ -37,15 +31,13 @@
* A fall-back mechanism is implemented in the deserializeAndSet methods to allow field values that don't match the
* target type to be preserved in the anySetter method if exists.
*/
public class SettableBeanPropertyDelegate extends SettableBeanProperty {
public class SettableBeanPropertyDelegate extends SettableBeanProperty.Delegating {

private final SettableBeanProperty delegate;
private final SettableAnyProperty anySetter;
private final transient BooleanSupplier useAnySetter;

SettableBeanPropertyDelegate(SettableBeanProperty delegate, SettableAnyProperty anySetter, BooleanSupplier useAnySetter) {
super(delegate);
this.delegate = delegate;
this.anySetter = anySetter;
this.useAnySetter = useAnySetter;
}
Expand All @@ -54,48 +46,8 @@ public class SettableBeanPropertyDelegate extends SettableBeanProperty {
* {@inheritDoc}
*/
@Override
public SettableBeanProperty withValueDeserializer(JsonDeserializer<?> deser) {
return new SettableBeanPropertyDelegate(delegate.withValueDeserializer(deser), anySetter, useAnySetter);
}

/**
* {@inheritDoc}
*/
@Override
public SettableBeanProperty withName(PropertyName newName) {
return new SettableBeanPropertyDelegate(delegate.withName(newName), anySetter, useAnySetter);
}

/**
* {@inheritDoc}
*/
@Override
public SettableBeanProperty withNullProvider(NullValueProvider nva) {
return new SettableBeanPropertyDelegate(delegate.withNullProvider(nva), anySetter, useAnySetter);
}

/**
* {@inheritDoc}
*/
@Override
public AnnotatedMember getMember() {
return delegate.getMember();
}

/**
* {@inheritDoc}
*/
@Override
public <A extends Annotation> A getAnnotation(Class<A> acls) {
return delegate.getAnnotation(acls);
}

/**
* {@inheritDoc}
*/
@Override
public void fixAccess(DeserializationConfig config) {
delegate.fixAccess(config);
protected SettableBeanProperty withDelegate(SettableBeanProperty d) {
return new SettableBeanPropertyDelegate(d, anySetter, useAnySetter);
}

/**
Expand Down Expand Up @@ -154,22 +106,6 @@ public Object deserializeSetAndReturn(JsonParser p, DeserializationContext ctxt,
return null;
}

/**
* {@inheritDoc}
*/
@Override
public void set(Object instance, Object value) throws IOException {
delegate.set(instance, value);
}

/**
* {@inheritDoc}
*/
@Override
public Object setAndReturn(Object instance, Object value) throws IOException {
return delegate.setAndReturn(instance, value);
}

private boolean shouldUseAnySetter() {
if (anySetter == null) {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,45 +57,48 @@ void setUp() {
@DisplayName("withValueDeserializer, should return a new instance")
void withValueDeserializer() {
// Given
doReturn(delegateMock).when(delegateMock).withValueDeserializer(any());
SettableBeanProperty secondDelegateMock = mock(SettableBeanProperty.class, RETURNS_DEEP_STUBS);
doReturn(secondDelegateMock).when(delegateMock).withValueDeserializer(any());
// When
final SettableBeanProperty result = settableBeanPropertyDelegate.withValueDeserializer(null);
// Then
assertThat(result)
.isInstanceOf(SettableBeanPropertyDelegate.class)
.isNotSameAs(settableBeanPropertyDelegate)
.hasFieldOrPropertyWithValue("anySetter", anySetterMock)
.hasFieldOrPropertyWithValue("delegate", delegateMock);
.hasFieldOrPropertyWithValue("delegate", secondDelegateMock);
}

@Test
@DisplayName("withName, should return a new instance")
void withName() {
// Given
doReturn(delegateMock).when(delegateMock).withName(any());
SettableBeanProperty secondDelegateMock = mock(SettableBeanProperty.class, RETURNS_DEEP_STUBS);
doReturn(secondDelegateMock).when(delegateMock).withName(any());
// When
final SettableBeanProperty result = settableBeanPropertyDelegate.withName(null);
// Then
assertThat(result)
.isInstanceOf(SettableBeanPropertyDelegate.class)
.isNotSameAs(settableBeanPropertyDelegate)
.hasFieldOrPropertyWithValue("anySetter", anySetterMock)
.hasFieldOrPropertyWithValue("delegate", delegateMock);
.hasFieldOrPropertyWithValue("delegate", secondDelegateMock);
}

@Test
@DisplayName("withNullProvider, should return a new instance")
void withNullProvider() {
// Given
doReturn(delegateMock).when(delegateMock).withNullProvider(any());
SettableBeanProperty secondDelegateMock = mock(SettableBeanProperty.class, RETURNS_DEEP_STUBS);
doReturn(secondDelegateMock).when(delegateMock).withNullProvider(any());
// When
final SettableBeanProperty result = settableBeanPropertyDelegate.withNullProvider(null);
// Then
assertThat(result)
.isInstanceOf(SettableBeanPropertyDelegate.class)
.isNotSameAs(settableBeanPropertyDelegate)
.hasFieldOrPropertyWithValue("anySetter", anySetterMock)
.hasFieldOrPropertyWithValue("delegate", delegateMock);
.hasFieldOrPropertyWithValue("delegate", secondDelegateMock);
}

@Test
Expand All @@ -109,6 +112,17 @@ void getMember() {
assertThat(result).isEqualTo("the-member");
}

@Test
@DisplayName("getCreatorIndex, should return delegate's creator index")
void getCreatorIndex() {
// Given
when(delegateMock.getCreatorIndex()).thenReturn(3);
// When
final int result = settableBeanPropertyDelegate.getCreatorIndex();
// Then
assertThat(result).isEqualTo(3);
}

@Test
@DisplayName("getAnnotation, should return delegate's Annotation")
void getAnnotation() {
Expand Down

0 comments on commit c21f267

Please sign in to comment.