Skip to content

Commit 0aef8c6

Browse files
committed
DATACMNS-283 - MappingInstantiationException captures more context now.
If an exception occurs in ReflectionEntityInstantiator we now capture more context about the failed instantiation.
1 parent e093aea commit 0aef8c6

File tree

3 files changed

+143
-13
lines changed

3 files changed

+143
-13
lines changed

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import java.lang.reflect.Array;
1919
import java.util.ArrayList;
20+
import java.util.Collections;
2021
import java.util.List;
2122

2223
import org.springframework.beans.BeanInstantiationException;
@@ -60,7 +61,7 @@ public <T, E extends PersistentEntity<? extends T, P>, P extends PersistentPrope
6061
return BeanUtils.instantiateClass(entity.getType());
6162
}
6263
} catch (BeanInstantiationException e) {
63-
throw new MappingInstantiationException(e.getMessage(), e);
64+
new MappingInstantiationException(entity, Collections.emptyList(), e);
6465
}
6566
}
6667

@@ -74,7 +75,7 @@ public <T, E extends PersistentEntity<? extends T, P>, P extends PersistentPrope
7475
try {
7576
return BeanUtils.instantiateClass(constructor.getConstructor(), params.toArray());
7677
} catch (BeanInstantiationException e) {
77-
throw new MappingInstantiationException(e.getMessage(), e);
78+
throw new MappingInstantiationException(entity, params, e);
7879
}
7980
}
8081
}
Lines changed: 89 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,114 @@
11
/*
2-
* Copyright (c) 2011 by the original author(s).
2+
* Copyright 2011-2013 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
66
* You may obtain a copy of the License at
77
*
8-
* http://www.apache.org/licenses/LICENSE-2.0
8+
* http://www.apache.org/licenses/LICENSE-2.0
99
*
1010
* Unless required by applicable law or agreed to in writing, software
1111
* distributed under the License is distributed on an "AS IS" BASIS,
1212
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
1716
package org.springframework.data.mapping.model;
1817

18+
import java.lang.reflect.Constructor;
19+
import java.util.List;
20+
21+
import org.springframework.data.mapping.PersistentEntity;
22+
import org.springframework.data.mapping.PreferredConstructor;
23+
import org.springframework.util.StringUtils;
24+
1925
/**
20-
* @author Jon Brisbin <[email protected]>
26+
* Exception being thrown in case an entity could not be instantiated in the process of a to-object-mapping.
27+
*
28+
* @author Oliver Gierke
29+
* @author Jon Brisbin
2130
*/
2231
public class MappingInstantiationException extends RuntimeException {
2332

33+
private static final long serialVersionUID = 822211065035487628L;
34+
private static final String TEXT_TEMPLATE = "Failed to instantiate %s using constructor %s with arguments %s";
35+
36+
private final Class<?> entityType;
37+
private final Constructor<?> constructor;
38+
private final List<Object> constructorArguments;
39+
40+
/**
41+
* Creates a {@link MappingInstantiationException} using the given message and cause.
42+
*
43+
* @deprecated use {@link #MappingInstantiationException(PersistentEntity, List, String, Exception)} instead.
44+
* @param message
45+
* @param cause
46+
*/
47+
@Deprecated
48+
public MappingInstantiationException(String message, Exception cause) {
49+
this(null, null, message, cause);
50+
}
51+
52+
/**
53+
* Creates a new {@link MappingInstantiationException} for the given {@link PersistentEntity}, constructor arguments
54+
* and the causing exception.
55+
*
56+
* @param entity
57+
* @param arguments
58+
* @param cause
59+
*/
60+
public MappingInstantiationException(PersistentEntity<?, ?> entity, List<Object> arguments, Exception cause) {
61+
this(entity, arguments, null, cause);
62+
}
63+
64+
private MappingInstantiationException(PersistentEntity<?, ?> entity, List<Object> arguments, String message,
65+
Exception cause) {
66+
67+
super(buildExceptionMessage(entity, arguments, null), cause);
68+
69+
this.entityType = entity == null ? null : entity.getType();
70+
this.constructor = entity == null || entity.getPersistenceConstructor() == null ? null : entity
71+
.getPersistenceConstructor().getConstructor();
72+
this.constructorArguments = arguments;
73+
}
74+
75+
private static final String buildExceptionMessage(PersistentEntity<?, ?> entity, List<Object> arguments,
76+
String defaultMessage) {
77+
78+
if (entity == null) {
79+
return defaultMessage;
80+
}
81+
82+
PreferredConstructor<?, ?> constructor = entity.getPersistenceConstructor();
83+
84+
return String.format(TEXT_TEMPLATE, entity.getType().getName(), constructor == null ? "NO_CONSTRUCTOR"
85+
: constructor.getConstructor().toString(), StringUtils.collectionToCommaDelimitedString(arguments));
86+
}
87+
2488
/**
25-
*
89+
* Returns the type of the entity that was attempted to instantiate.
90+
*
91+
* @return the entityType
2692
*/
27-
private static final long serialVersionUID = 1L;
93+
public Class<?> getEntityType() {
94+
return entityType;
95+
}
2896

29-
public MappingInstantiationException(String s, Throwable throwable) {
30-
super(s, throwable);
97+
/**
98+
* The constructor used during the instantiation attempt.
99+
*
100+
* @return the constructor
101+
*/
102+
public Constructor<?> getConstructor() {
103+
return constructor;
31104
}
32105

106+
/**
107+
* The constructor arguments used to invoke the constructor.
108+
*
109+
* @return the constructorArguments
110+
*/
111+
public List<Object> getConstructorArguments() {
112+
return constructorArguments;
113+
}
33114
}

src/test/java/org/springframework/data/convert/ReflectionEntityInstantiatorUnitTests.java

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012 the original author or authors.
2+
* Copyright 2012-2013 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,22 +19,27 @@
1919
import static org.junit.Assert.*;
2020
import static org.mockito.Mockito.*;
2121
import static org.springframework.data.convert.ReflectionEntityInstantiator.*;
22+
import static org.springframework.data.util.ClassTypeInformation.*;
2223

24+
import java.lang.reflect.Constructor;
2325
import java.lang.reflect.Field;
26+
import java.util.Arrays;
27+
import java.util.List;
2428

2529
import org.junit.Test;
2630
import org.junit.runner.RunWith;
2731
import org.mockito.Mock;
32+
import org.mockito.Mockito;
2833
import org.mockito.runners.MockitoJUnitRunner;
2934
import org.springframework.data.convert.ReflectionEntityInstantiatorUnitTests.Outer.Inner;
3035
import org.springframework.data.mapping.PersistentEntity;
3136
import org.springframework.data.mapping.PersistentProperty;
3237
import org.springframework.data.mapping.PreferredConstructor;
3338
import org.springframework.data.mapping.PreferredConstructor.Parameter;
3439
import org.springframework.data.mapping.model.BasicPersistentEntity;
40+
import org.springframework.data.mapping.model.MappingInstantiationException;
3541
import org.springframework.data.mapping.model.ParameterValueProvider;
3642
import org.springframework.data.mapping.model.PreferredConstructorDiscoverer;
37-
import org.springframework.data.util.ClassTypeInformation;
3843
import org.springframework.util.ReflectionUtils;
3944
import org.springframework.util.ReflectionUtils.FieldCallback;
4045

@@ -92,7 +97,7 @@ public void instantiatesTypeWithPreferredConstructorUsingParameterValueProvider(
9297
@Test
9398
public void createsInnerClassInstanceCorrectly() {
9499

95-
BasicPersistentEntity<Inner, P> entity = new BasicPersistentEntity<Inner, P>(ClassTypeInformation.from(Inner.class));
100+
BasicPersistentEntity<Inner, P> entity = new BasicPersistentEntity<Inner, P>(from(Inner.class));
96101
PreferredConstructor<Inner, P> constructor = entity.getPersistenceConstructor();
97102
Parameter<Object, P> parameter = constructor.getParameters().iterator().next();
98103

@@ -114,6 +119,37 @@ public void doWith(Field field) throws IllegalArgumentException, IllegalAccessEx
114119
});
115120
}
116121

122+
/**
123+
* @see DATACMNS-283
124+
*/
125+
@Test
126+
@SuppressWarnings({ "unchecked", "rawtypes" })
127+
public void capturesContextOnInstantiationException() throws Exception {
128+
129+
PersistentEntity<Sample, P> entity = new BasicPersistentEntity<Sample, P>(from(Sample.class));
130+
131+
when(provider.getParameterValue(Mockito.any(Parameter.class))).thenReturn("FOO");
132+
133+
Constructor constructor = Sample.class.getConstructor(Long.class, String.class);
134+
List<Object> parameters = Arrays.asList((Object) "FOO", (Object) "FOO");
135+
136+
try {
137+
INSTANCE.createInstance(entity, provider);
138+
fail("Expected MappingInstantiationException!");
139+
} catch (MappingInstantiationException o_O) {
140+
141+
assertThat(o_O.getConstructor(), is(constructor));
142+
assertThat(o_O.getConstructorArguments(), is(parameters));
143+
assertEquals(Sample.class, o_O.getEntityType());
144+
145+
assertThat(o_O.getMessage(), containsString(Sample.class.getName()));
146+
assertThat(o_O.getMessage(), containsString(Long.class.getName()));
147+
assertThat(o_O.getMessage(), containsString(String.class.getName()));
148+
assertThat(o_O.getMessage(), containsString("FOO"));
149+
System.out.println(o_O.getMessage());
150+
}
151+
}
152+
117153
static class Foo {
118154

119155
Foo(String foo) {
@@ -127,4 +163,16 @@ class Inner {
127163

128164
}
129165
}
166+
167+
static class Sample {
168+
169+
final Long id;
170+
final String name;
171+
172+
public Sample(Long id, String name) {
173+
174+
this.id = id;
175+
this.name = name;
176+
}
177+
}
130178
}

0 commit comments

Comments
 (0)