Skip to content

Commit c6d8803

Browse files
mp911deodrotbohm
authored andcommitted
DATACMNS-1200 - Polishing.
Throw MappingInstantiationException from KotlinClassGeneratingEntityInstantiator if instantiation fails to align behavior with ClassGeneratingEntityInstantiator. Report Kotlin constructor instead of Java constructor if available. Original pull request: #255.
1 parent f45e2be commit c6d8803

File tree

3 files changed

+54
-3
lines changed

3 files changed

+54
-3
lines changed

src/main/java/org/springframework/data/convert/KotlinClassGeneratingEntityInstantiator.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,15 @@
2020
import kotlin.reflect.jvm.ReflectJvmMapping;
2121

2222
import java.lang.reflect.Constructor;
23+
import java.util.Arrays;
2324
import java.util.List;
2425
import java.util.stream.IntStream;
2526

2627
import org.springframework.data.mapping.PersistentEntity;
2728
import org.springframework.data.mapping.PersistentProperty;
2829
import org.springframework.data.mapping.PreferredConstructor;
2930
import org.springframework.data.mapping.PreferredConstructor.Parameter;
31+
import org.springframework.data.mapping.model.MappingInstantiationException;
3032
import org.springframework.data.mapping.model.ParameterValueProvider;
3133
import org.springframework.data.util.ReflectionUtils;
3234
import org.springframework.lang.Nullable;
@@ -197,7 +199,17 @@ static class DefaultingKotlinClassInstantiatorAdapter implements EntityInstantia
197199
public <T, E extends PersistentEntity<? extends T, P>, P extends PersistentProperty<P>> T createInstance(E entity,
198200
ParameterValueProvider<P> provider) {
199201

200-
PreferredConstructor<? extends T, P> preferredConstructor = entity.getPersistenceConstructor();
202+
Object[] params = extractInvocationArguments(entity.getPersistenceConstructor(), provider);
203+
204+
try {
205+
return (T) instantiator.newInstance(params);
206+
} catch (Exception e) {
207+
throw new MappingInstantiationException(entity, Arrays.asList(params), e);
208+
}
209+
}
210+
211+
private <P extends PersistentProperty<P>, T> Object[] extractInvocationArguments(
212+
@Nullable PreferredConstructor<? extends T, P> preferredConstructor, ParameterValueProvider<P> provider) {
201213

202214
if (preferredConstructor == null) {
203215
throw new IllegalArgumentException("PreferredConstructor must not be null!");
@@ -237,7 +249,7 @@ public <T, E extends PersistentEntity<? extends T, P>, P extends PersistentPrope
237249
params[userParameterCount + i] = defaulting[i];
238250
}
239251

240-
return (T) instantiator.newInstance(params);
252+
return params;
241253
}
242254
}
243255
}

src/main/java/org/springframework/data/mapping/model/MappingInstantiationException.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,17 @@
1515
*/
1616
package org.springframework.data.mapping.model;
1717

18+
import kotlin.reflect.KFunction;
19+
import kotlin.reflect.jvm.ReflectJvmMapping;
20+
1821
import java.lang.reflect.Constructor;
1922
import java.util.ArrayList;
2023
import java.util.List;
2124
import java.util.Optional;
2225

2326
import org.springframework.data.mapping.PersistentEntity;
2427
import org.springframework.data.mapping.PreferredConstructor;
28+
import org.springframework.data.util.ReflectionUtils;
2529
import org.springframework.lang.Nullable;
2630
import org.springframework.util.ObjectUtils;
2731

@@ -88,12 +92,28 @@ private static String buildExceptionMessage(Optional<PersistentEntity<?, ?>> ent
8892
}
8993

9094
return String.format(TEXT_TEMPLATE, it.getType().getName(),
91-
constructor.map(c -> c.getConstructor().toString()).orElse("NO_CONSTRUCTOR"), //
95+
constructor.map(c -> toString(c)).orElse("NO_CONSTRUCTOR"), //
9296
String.join(",", toStringArgs));
9397

9498
}).orElse(defaultMessage);
9599
}
96100

101+
private static String toString(PreferredConstructor<?, ?> preferredConstructor) {
102+
103+
Constructor<?> constructor = preferredConstructor.getConstructor();
104+
105+
if (ReflectionUtils.isSupportedKotlinClass(constructor.getDeclaringClass())) {
106+
107+
KFunction<?> kotlinFunction = ReflectJvmMapping.getKotlinFunction(constructor);
108+
109+
if (kotlinFunction != null) {
110+
return kotlinFunction.toString();
111+
}
112+
}
113+
114+
return constructor.toString();
115+
}
116+
97117
/**
98118
* Returns the type of the entity that was attempted to instantiate.
99119
*

src/test/kotlin/org/springframework/data/convert/KotlinClassGeneratingEntityInstantiatorUnitTests.kt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@ import org.mockito.Mock
2525
import org.mockito.junit.MockitoJUnitRunner
2626
import org.springframework.data.mapping.PersistentEntity
2727
import org.springframework.data.mapping.context.SamplePersistentProperty
28+
import org.springframework.data.mapping.model.MappingInstantiationException
2829
import org.springframework.data.mapping.model.ParameterValueProvider
2930
import org.springframework.data.mapping.model.PreferredConstructorDiscoverer
31+
import java.lang.IllegalArgumentException
3032

3133
/**
3234
* Unit tests for [KotlinClassGeneratingEntityInstantiator] creating instances using Kotlin data classes.
@@ -79,6 +81,21 @@ class KotlinClassGeneratingEntityInstantiatorUnitTests {
7981
Assertions.assertThat(instance.prop34).isEqualTo("White")
8082
}
8183

84+
@Test // DATACMNS-1200
85+
fun `absent primitive value should cause MappingInstantiationException`() {
86+
87+
val entity = this.entity as PersistentEntity<WithBoolean, SamplePersistentProperty>
88+
val constructor = PreferredConstructorDiscoverer.discover<WithBoolean, SamplePersistentProperty>(WithBoolean::class.java)
89+
90+
doReturn(constructor).whenever(entity).persistenceConstructor
91+
doReturn(constructor.constructor.declaringClass).whenever(entity).type
92+
93+
Assertions.assertThatThrownBy { KotlinClassGeneratingEntityInstantiator().createInstance(entity, provider) } //
94+
.isInstanceOf(MappingInstantiationException::class.java) //
95+
.hasMessageContaining("fun <init>(kotlin.Boolean)") //
96+
.hasCauseInstanceOf(IllegalArgumentException::class.java)
97+
}
98+
8299
data class Contact(val firstname: String, val lastname: String)
83100

84101
data class ContactWithDefaulting(val prop0: String, val prop1: String = "White", val prop2: String,
@@ -94,5 +111,7 @@ class KotlinClassGeneratingEntityInstantiatorUnitTests {
94111
val prop30: String = "White", val prop31: String = "White", val prop32: String = "White",
95112
val prop33: String, val prop34: String = "White"
96113
)
114+
115+
data class WithBoolean(val state: Boolean)
97116
}
98117

0 commit comments

Comments
 (0)