Skip to content

Commit 7d26b46

Browse files
committed
Fix #3125
1 parent f550e22 commit 7d26b46

File tree

4 files changed

+134
-37
lines changed

4 files changed

+134
-37
lines changed

release-notes/VERSION-2.x

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ Project: jackson-databind
3030
#3117: Use more limiting default visibility settings for JDK types (java.*, javax.*)
3131
#3122: Deep merge for `JsonNode` using `ObjectReader.readTree()`
3232
(reported by Eric S)
33+
#3125: IllegalArgumentException: Conflicting setter definitions for property
34+
with more than 2 setters
35+
(reported by mistyzyq@github)
3336
#3130: Serializing java.lang.Thread fails on JDK 11 and above (should suppress
3437
serialization of ClassLoader)
3538
- Fix to avoid problem with `BigDecimalNode`, scale of `Integer.MIN_VALUE` (see

src/main/java/com/fasterxml/jackson/databind/introspect/POJOPropertyBuilder.java

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.fasterxml.jackson.databind.introspect;
22

33
import java.util.*;
4+
import java.util.stream.Collectors;
45

56
import com.fasterxml.jackson.annotation.JsonInclude;
67
import com.fasterxml.jackson.annotation.JsonProperty;
@@ -483,6 +484,59 @@ public AnnotatedMethod getSetter()
483484
return curr.value;
484485
}
485486

487+
/**
488+
* Helper method called in cases where we have encountered two setter methods
489+
* that have same precedence and cannot be resolved. This does not yet necessarily
490+
* mean a failure since it is possible something with a higher precedence could
491+
* still be found; handling is just separated into separate method for convenience.
492+
*
493+
* @param curr
494+
* @param next
495+
*
496+
* @return Chosen setter method, if any
497+
*
498+
* @throws IllegalArgumentException If conflict could not be resolved
499+
*
500+
* @since 2.13
501+
*/
502+
protected AnnotatedMethod _selectSetterFromMultiple(Linked<AnnotatedMethod> curr,
503+
Linked<AnnotatedMethod> next)
504+
{
505+
// First: store reference to the initial possible conflict
506+
List<AnnotatedMethod> conflicts = new ArrayList<>();
507+
conflicts.add(curr.value);
508+
conflicts.add(next.value);
509+
510+
next = next.next;
511+
for (; next != null; next = next.next) {
512+
AnnotatedMethod selected = _selectSetter(curr.value, next.value);
513+
if (selected == curr.value) {
514+
// No change, next was lower-precedence
515+
continue;
516+
}
517+
if (selected == next.value) {
518+
// Hooray! Found a higher-priority one; clear conflict list
519+
conflicts.clear();
520+
curr = next;
521+
continue;
522+
}
523+
// Tie means one more non-resolved, add
524+
conflicts.add(next.value);
525+
}
526+
527+
// It is possible we resolved it; if so:
528+
if (conflicts.isEmpty()) {
529+
_setters = curr.withoutNext();
530+
return curr.value;
531+
}
532+
// Otherwise
533+
String desc = conflicts.stream().map(AnnotatedMethod::getFullName)
534+
.collect(Collectors.joining(" vs "));
535+
throw new IllegalArgumentException(String.format(
536+
"Conflicting setter definitions for property \"%s\": %s",
537+
getName(), desc));
538+
}
539+
486540
// @since 2.13
487541
protected AnnotatedMethod _selectSetter(AnnotatedMethod currM, AnnotatedMethod nextM)
488542
{
@@ -521,15 +575,6 @@ protected AnnotatedMethod _selectSetter(AnnotatedMethod currM, AnnotatedMethod n
521575
: _annotationIntrospector.resolveSetterConflict(_config, currM, nextM);
522576
}
523577

524-
// @since 2.13
525-
protected AnnotatedMethod _selectSetterFromMultiple(Linked<AnnotatedMethod> curr,
526-
Linked<AnnotatedMethod> next)
527-
{
528-
throw new IllegalArgumentException(String.format(
529-
"Conflicting setter definitions for property \"%s\": %s vs %s",
530-
getName(), curr.value.getFullName(), next.value.getFullName()));
531-
}
532-
533578
@Override
534579
public AnnotatedField getField()
535580
{

src/test/java/com/fasterxml/jackson/databind/introspect/POJOPropertiesCollectorTest.java

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -231,14 +231,6 @@ public DuplicateGetterCreatorBean(@JsonProperty("bloop") @A boolean bloop) {}
231231
public boolean getBloop() { return true; }
232232
}
233233

234-
// [databind#3125]: As per existing (2.7+) logic we SHOULD tie-break
235-
// in favor of `String` but code up until 2.12 short-circuited early fail
236-
static class DupSetter3125Bean {
237-
public void setValue(Integer value) { }
238-
public void setValue(Boolean value) { }
239-
public void setValue(String value) { }
240-
}
241-
242234
/*
243235
/**********************************************************
244236
/* Unit tests
@@ -500,22 +492,7 @@ public void testDuplicateGettersCreator() throws Exception
500492
assertNotNull(prop._getters.next);
501493
assertTrue(prop._getters.next.value.hasAnnotation(A.class));
502494
}
503-
/*
504-
// [databind#3125]
505-
public void testDuplicateSetters() throws Exception
506-
{
507-
POJOPropertiesCollector coll = collector(MAPPER, DupSetter3125Bean.class,
508-
false);
509-
final List<BeanPropertyDefinition> props = coll.getProperties();
510-
assertEquals(1, props.size());
511-
POJOPropertyBuilder prop = (POJOPropertyBuilder) props.get(0);
512-
assertEquals("value", prop.getName());
513-
// but this failed
514-
AnnotatedMethod m = prop.getSetter();
515-
assertNotNull(m);
516-
assertEquals(String.class, m.getRawParameterType(0));
517-
}
518-
*/
495+
519496
private void _verifyProperty(BeanDescription beanDesc,
520497
boolean verifyDesc, boolean verifyIndex, String expDefaultValue)
521498
{
@@ -551,10 +528,11 @@ private void _verifyProperty(BeanDescription beanDesc,
551528
}
552529
}
553530
}
531+
554532
/*
555-
/**********************************************************
533+
/**********************************************************************
556534
/* Helper methods
557-
/**********************************************************
535+
/**********************************************************************
558536
*/
559537

560538
protected POJOPropertiesCollector collector(ObjectMapper m0,

src/test/java/com/fasterxml/jackson/databind/introspect/SetterConflictTest.java

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
package com.fasterxml.jackson.databind.introspect;
22

3+
import java.util.List;
4+
35
import com.fasterxml.jackson.annotation.JsonSetter;
6+
47
import com.fasterxml.jackson.databind.*;
8+
import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
59

610
// mostly for [databind#1033]
711
public class SetterConflictTest extends BaseMapTest
@@ -30,10 +34,26 @@ public void setBloop(Object bloop) {
3034
}
3135
}
3236

37+
// [databind#3125]: As per existing (2.7+) logic we SHOULD tie-break
38+
// in favor of `String` but code up until 2.12 short-circuited early fail
39+
static class DupSetter3125Bean {
40+
String str;
41+
42+
public void setValue(Integer value) { throw new RuntimeException("Integer: wrong!"); }
43+
public void setValue(Boolean value) { throw new RuntimeException("Boolean: wrong!"); }
44+
public void setValue(String value) { str = value; }
45+
}
46+
47+
static class DupSetter3125BeanFail {
48+
public void setValue(Integer value) { throw new RuntimeException("Integer: wrong!"); }
49+
public void setValue(Boolean value) { throw new RuntimeException("Boolean: wrong!"); }
50+
public void setValue(List<String> value) { throw new RuntimeException("List: wrong!"); }
51+
}
52+
3353
/*
34-
/**********************************************************
54+
/**********************************************************************
3555
/* Test methods
36-
/**********************************************************
56+
/**********************************************************************
3757
*/
3858

3959
private final ObjectMapper MAPPER = newJsonMapper();
@@ -56,4 +76,55 @@ public void testConflictingSetters() throws Exception
5676
DuplicateSetterBean2979.class);
5777
assertEquals(Boolean.TRUE, result.value);
5878
}
79+
80+
// [databind#3125]
81+
public void testDuplicateSetterResolutionOk() throws Exception
82+
{
83+
POJOPropertiesCollector coll = collector(MAPPER, DupSetter3125Bean.class,
84+
false);
85+
final List<BeanPropertyDefinition> props = coll.getProperties();
86+
assertEquals(1, props.size());
87+
POJOPropertyBuilder prop = (POJOPropertyBuilder) props.get(0);
88+
assertEquals("value", prop.getName());
89+
// but this failed
90+
AnnotatedMethod m = prop.getSetter();
91+
assertNotNull(m);
92+
assertEquals(String.class, m.getRawParameterType(0));
93+
94+
// and then actual usage too
95+
DupSetter3125Bean value = MAPPER.readValue(a2q("{'value':'foo'}"),
96+
DupSetter3125Bean.class);
97+
assertEquals("foo", value.str);
98+
}
99+
100+
// [databind#3125]: caught case
101+
public void testDuplicateSetterResolutionFail() throws Exception
102+
{
103+
try {
104+
MAPPER.readValue(a2q("{'value':'foo'}"),
105+
DupSetter3125BeanFail.class);
106+
fail("Should not pass");
107+
} catch (InvalidDefinitionException e) {
108+
verifyException(e, "Conflicting setter definitions for property \"value\"");
109+
}
110+
}
111+
112+
/*
113+
/**********************************************************************
114+
/* Helper methods
115+
/**********************************************************************
116+
*/
117+
118+
protected POJOPropertiesCollector collector(ObjectMapper m0,
119+
Class<?> cls, boolean forSerialization)
120+
{
121+
BasicClassIntrospector bci = new BasicClassIntrospector();
122+
// no real difference between serialization, deserialization, at least here
123+
if (forSerialization) {
124+
return bci.collectProperties(m0.getSerializationConfig(),
125+
m0.constructType(cls), null, true);
126+
}
127+
return bci.collectProperties(m0.getDeserializationConfig(),
128+
m0.constructType(cls), null, false);
129+
}
59130
}

0 commit comments

Comments
 (0)